pwcbsd000755 000423 000000 00000000000 10553463415 012525 5ustar00luigiwheel000000 000000 pwcbsd/usb000755 000423 000000 00000000000 10551700025 013303 5ustar00luigiwheel000000 000000 pwcbsd/README000644 000423 000000 00000005457 10553461760 013500 0ustar00luigiwheel000000 000000 # $Id: README,v 1.5 2007/01/17 09:13:15 luigi Exp $ documentation on the pwcbsd driver and extensions --- 1. sources of information --- The pwcbsd driver comes from ports/multimedia/pwcbsd The spca driver comes from http://mxhaard.free.fr/spca50x/Download/spca5xx-20060501.tar.gz but a newer one is at http://mxhaard.free.fr/spca50x/Download/gspcav1-20070110.tar.gz The linux-uvc driver comes from svn checkout svn://svn.berlios.de/linux-uvc/linux-uvc/trunk The newusb driver comes from http://perforce.freebsd.org/depotTreeBrowser.cgi?FSPC=//depot/projects/usb/src/sys/dev/usb The quickcam-ga driver comes from ports/graphics/qcamview --- 2. documentation on the pwc driver functions ---- (these are working notes, sometimes stale. The source may contain more up-to-date comments, so double check there as well). USB_MATCH: check vendor and id against table USB_ATTACH check again, then initialize descriptor fields based on the resources. pwc-misc.c::pwc_construct() initialize supported formats depending on camera type. For spca, call spca5xx::spca50x_configure() bridge-specific config code e.g. sonix_config() spca50x_set_packet_size(x, 0) initialize pipe size with spca5xx_getDefaultMode() or similar spca50x_configure_sensor(): set frame sizes pwc-ctrl.c::pwc_get_cmos_sensor(): sensor-specific call to detect the sensor type if needed. pwc_open(): pwc_camera_power(sc, 1): power on if necessary pwc_set_lets(sc, ...) set led if necessary allocate and initialize buffers allocate decompressor table allocate isoc transfer buffers set a fallback video mode 5. pwc.c::pwc_try_video_mode(): initialize video mode pwc-ctrl.c:int pwc_set_video_mode() call camera-specific video mode e.g. spca50x_set_packet_size() determines valternate depending on pipe_size which is set in spca5xx_getDefaultMode() or spca5xx_setMode() determines vendpoint depending on bridge set_video_mode_Nala() send specific commands to the camera to set fps and size identifies the valternate endpoint set_video_mode_Timon() set_video_mode_Kiara() initialize usbd interface ? 5. equivalent to calling spwc5xx.c::spca50x_init_source() { call bridge-specific init code call spca5xx_setMode() or spca50x_find_mode_index() spca50x_set_mode() XXX different from spca5xx_setMode() } spwc5xx.c::spwc5xx::spcaCameraStart() call bridge-specific start code spca50x->streaming = 1; . ------------ in pwc_try_video_mode() if (sc->type == 0) { /* spca camera */ struct usb_spca50x *spca50x = &sc->spca50x; err = -spca50x_init_source(spca50x); if (err != 0) goto bad; spcaCameraStart(spca50x); spca50x->streaming = 1; } pwcbsd/README.newusb000644 000423 000000 00000037342 10551457104 014773 0ustar00luigiwheel000000 000000 DESCRIPTION OF THE NEW USB API The new USB 2.0 API consists of 4 functions. All transfer types are managed using these functions. There is no longer need for separate functions to setup INTERRUPT- and ISOCHRONOUS- transfers. +--------------------------------------------------------------+ | | | "usbd_transfer_setup" - This function will allocate all | | necessary DMA memory and might | | sleep! | | | | "usbd_transfer_unsetup" - This function will stop the USB | | transfer, if it is currently | | active and release all DMA | | memory. | | | | "usbd_transfer_start" - This function will start a USB | | transfer, if not already started.| | This function is always non- | | blocking, except when the USB | | transfer is SYNCHRONOUS. ** | | | | "usbd_transfer_stop" - This function will stop a USB | | transfer, if not already stopped.| | The callback function will be | | called before this function | | returns. This function is always | | non-blocking. ** | | | | ** These functions must be called with the private driver's | | lock locked. | | | +--------------------------------------------------------------+ Reference: /sys/dev/usb/usb_transfer.c One must setup the USB transfer, struct usbd_xfer, from the callback handler, which is required for non-blocking operation, in the end. This behavior is also very practical when writing USB device drivers, because it is easy to make a loop, starting the next transfer from the previous. Simply reorder the labels! The callback's lock is locked by the caller. This solves synchronization problems related to stopping USB transfers. /* * A simple USB callback state-machine: * * +->-----------------------+ * | | * +-<-+-------[tr_setup]--------+-<-+-<-[start/restart] * | | * | | * | | * +------>-[tr_transferred]---------+ * | | * +--------->-[tr_error]------------+ */ void usbd_default_callback(struct usbd_xfer *xfer) { /* NOTE: it is not allowed to return * before "USBD_CHECK_STATUS()", * even if the system is tearing down! */ USBD_CHECK_STATUS(xfer); tr_setup: /* setup xfer->length, xfer->frlengths, xfer->nframes * and write data to xfer->buffer if any */ /**/ usbd_start_hardware(xfer); return; tr_transferred: /* read data from xfer->buffer if any */ tr_error: /* print error message */ return; } NOTE for control endpoints: all of the control transfer now resides in the buffer pointed to by "xfer->buffer", including the request. This is better and saves memory. 1) Something that one should be aware of is that, all USB callbacks support recursation. That means one can start/stop whatever transfer from the callback of another transfer one desires. Also the transfer that is currently called back. Recursion is handled like this, that when the callback that wants to recurse returns, it is called one more time. 2) After that the "tr_setup" label has been jumped in the callback, one can always depend on that "tr_error" or "tr_transferred" will get jumped afterwards. Always! 3) sleeping functions can only be called from the attach routine of the driver. Else one should not use sleeping functions unless one has to. It is very difficult with sleep, because one has to think that the device might have detached when it returns from sleep. USB device driver examples: /sys/dev/usb/ugen.c /sys/dev/usb/ulpt.c /sys/dev/usb/uhid.c /sys/dev/usb/... QUICK REFERENCE =============== /*------------------------------------------------------------------------* * usbd_status * usbd_transfer_setup(udev, iface_index, pxfer, setup_start, * n_setup, priv_sc, priv_mtx) *------------------------------------------------------------------------*/ - "udev" is a pointer to "struct usbd_device" - "iface_index" is the interface index number - "pxfer" is a pointer to an array of USB transfer pointers that are initialized to NULL, and then pointed to the allocated DMA-able USB transfers - "setup_start" is a pointer to an array of USB config structures - "n_setup" is a number telling the USB system how many USB transfers should be setup - "priv_sc" is the private softc pointer, which will be used to initialize "xfer->priv_sc" - "priv_mtx" is the private mutex protecting the transfer structure and the softc. This pointer is used to initialize "xfer->priv_mtx". /*------------------------------------------------------------------------* * void * usbd_transfer_unsetup(pxfer, n_setup) *------------------------------------------------------------------------*/ - "pxfer" is a pointer to an array of USB transfer pointers, that may be NULL, that should be freed by the USB system. - "n_setup" is a number telling the USB system how many USB transfers should be unsetup NOTE: This function can sleep, waiting for active mutexes to become unlocked! NOTE: It is not allowed to call "usbd_transfer_unsetup" from the callback of a USB transfer. /*------------------------------------------------------------------------* * void * usbd_transfer_start(xfer) *------------------------------------------------------------------------*/ - "xfer" is pointer to a USB transfer that should be started NOTE: this function must be called with "priv_mtx" locked /*------------------------------------------------------------------------* * void * usbd_transfer_stop(xfer) *------------------------------------------------------------------------*/ - "xfer" is a pointer to a USB transfer that should be stopped NOTE: this function must be called with "priv_mtx" locked NOTE: if the transfer was in progress, the callback will called with "xfer->error=USBD_CANCELLED", before this function returns /*------------------------------------------------------------------------* * struct usbd_config { * type, endpoint, direction, interval, timeout, frames, index * flags, bufsize, callback * }; *------------------------------------------------------------------------*/ - The "type" field selects the USB pipe type. Valid values are: UE_INTERRUPT, UE_CONTROL, UE_BULK, UE_ISOCHRONOUS. The special value UE_BULK_INTR will select BULK and INTERRUPT pipes. This field is mandatory. - The "endpoint" field selects the USB endpoint number. A value of 0xFF, "-1" or "UE_ADDR_ANY" will select the first matching endpoint. This field is mandatory. - The "direction" field selects the USB endpoint direction. A value of 0xFF, "-1" or "UE_DIR_ANY" will select the first matching endpoint. Else valid values are: "UE_DIR_IN" and "UE_DIR_OUT". This field is mandatory. - The "interval" field selects the interrupt interval, for "type" = UE_INTERRUPT. The "interval" is given in milliseconds. "0" selects the default interrupt interval. - The "timeout" field, if non-zero, will set the transfer timeout, in milliseconds. - The "frames" field sets the number of isochronous frames, for "type" = UE_ISOCHRONOUS. - The "index" field allows one to give a number, in case more endpoints match the description, that selects which matching "index" should be used. - The "flags" field allows one to set flags for the transfer. Valid flags are: USBD_SYNCHRONOUS This flag can only be used with the default callback, "usbd_default_callback()", and will cause the "usbd_transfer_start()" function to sleep, exiting all mutexes, until the transfer is finished. This flag is depreciated. USBD_FORCE_SHORT_XFER This flag forces the last USB packet sent to be short. A short packet has a length of less than "xfer->max_packet_size", which derives from "wMaxPacketSize". USBD_SHORT_XFER_OK This flag allows the transfer length, "xfer->actlen" to be less than "xfer->length", upon completion of a transfer. USBD_CUSTOM_CLEARSTALL USBD_USE_POLLING This flag can be used with any callback and will cause the "usbd_transfer_start()" function to wait, using "DELAY()", without exiting any mutexes, until the transfer is finished or has timed out. USBD_USE_DMA This flag will cause the USB host controller driver to not allocate a second data buffer, "xfer->buffer". Instead of transferring data using "xfer->buffer", data must be transferred by a call to "usbd_copy_in(&(xfer->buf_data), offset, src, len)" or "usbd_copy_out(&(xfer->buf_data), offset, dst, len)". This saves an extra data copy. - The "bufsize" field sets the total buffer size in bytes. If this field is zero, "wMaxPacketSize" will be used, multiplied by the "frames" field if the transfer type is isochronous. This is useful for setting up interrupt pipes. This field is mandatory. NOTE: For control transfers "bufsize" includes the length of the request structure. - The "callback" field sets the USB callback. This field is mandatory. MUTEX NOTE: =========== When you create a mutex, using "mtx_init()", don't forget to call "mtx_destroy()" at detach, else you can get "freed memory accessed" panics. --HPS -------------------------------------------------------------------- -------------------------------------------------------------------- -------------------- ADAPTING OLD DRIVERS -------------------------- -------------------------------------------------------------------- -------------------------------------------------------------------- The traditional FreeBSD USB drivers use a number of macros to adapt the code to the various *BSD versions. Additionally, some of the callbacks differ. Below are some notes on adapting the drivers ----- typedefs -------------------------------------- The following typedefs can be useful in porting /* wrapper for the thread/proc struct * argument to various functions */ typedef struct thread *usb_proc_ptr; /* This is for convenience only */ typedef void *usbd_private_handle; /* * usbd_xfer_handle is a shorthand for struct usbd_xfer. * The new struct now has a pointer * struct usbd_device * udev; * void * priv_sc; // replaces usb_sc */ typedef struct usbd_xfer *usbd_xfer_handle; /* * The major change in struct usbd_device is that the * struct usbd_pipe default_pipe; * is not a pointer anymore (it used to point to malloc'ed * memory in the old driver). * Similarly, these are now arrays not pointers: * device_t subdevs[USB_MAX_ENDPOINTS]; * struct usbd_interface ifaces[USB_MAX_ENDPOINTS]; * struct usbd_pipe pipes[USB_MAX_ENDPOINTS]; */ typedef struct usbd_device *usbd_device_handle; /* * The struct usbd_interface is now a lot smaller */ typedef struct usbd_interface *usbd_interface_handle; /* * The pipe also has not a list of pipes anymore */ typedef struct usbd_pipe usbd_pipe_handle; /* XXX */ /* The callback is completely different now */ typedef void (*usbd_callback)(usbd_xfer_handle, usbd_private_handle, usbd_status); ----- functions -------------------------------------- /* * usbd_get_string() is replaced by usbreq_get_string_any() which * has the buffer size as an additional argument. The following might help: */ #define usbd_get_string(udev, si, buf) \ usbreq_get_string_any(udev, si, buf, sizeof(buf)) /* * usbd_endpoint_count() is not available anymore, but you can * fetch the value directly e.g. */ #define usbd_endpoint_count(iface, res) \ ( ((*(res)) = (iface)->idesc->bNumEndpoints) , USBD_NORMAL_COMPLETION ) /* * usbd_set_interface() now requires a pointer to the device, which * is not in the interface_handle anymore. If struct foo_softc *sc is * available, you can replace it as follows: */ #define usbd_set_interface(iface, alt) \ usbreq_set_interface(sc->udev, iface, alt) ----- various macros -------------------------------------- #define FOOUNIT(dev) (minor(dev)) this is customary to get the unit number from a device_t USB_DECLARE_DRIVER(foo); --> This macro is used to declare the prototypes for the various functions used in the driver. Can be replaced by static device_probe_t foo_match; static device_attach_t foo_attach; static device_detach_t foo_detach; static devclass_t foo_devclass; static device_method_t foo_methods[] = { DEVMETHOD(device_probe, foo_match), /* or foo_probe if you like */ DEVMETHOD(device_attach, foo_attach), DEVMETHOD(device_detach, foo_detach), {0,0}, /* init */ {0,0} /* terminator if not supplied before */ }; static driver_t foo_driver = { "foo", foo_methods, sizeof(struct foo_softc) }; MODULE_DEPEND(foo, usb, 1, 1, 1); USB_MATCH(foo) { USB_MATCH_START(foo, uaa); --> These macros can be replaced by static int foo_match(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); USB_ATTACH(foo) { USB_ATTACH_START(foo, sc, uaa); --> can be replaced by static int pwc_attach(device_t dev) { struct pwc_softc *sc = device_get_softc(dev); struct usb_attach_arg *uaa = device_get_ivars(dev); USB_DETACH(ulpt) { USB_DETACH_START(ulpt, sc); --> can be replaced by static int pwc_detach(device_t dev) { struct pwc_softc *sc = device_get_softc(dev); int foo_open(struct cdev *dev, int flag, int mode, usb_proc_ptr p) { struct foo_softc *sc; USB_GET_SC_OPEN(foo, FOOUNIT(dev), sc); --> replace with int foo_open(struct cdev *dev, int flag, int mode, usb_proc_ptr p) { int unit = FOOUNIT(dev); struct foo_softc *sc = devclass_get_softc(foo_devclass, unit); if (sc == NULL) return ENXIO; ... USB_GET_SC(foo, FOOUNIT(dev), sc); --> this is used within the handlers(read, write etc) as a replacement for sc = devclass_get_softc(foo_devclass, FOOUNIT(dev)); ------------------------------------------------------------------- UNMAPPED FUNCTIONS For the following functions a replacement has not been identified yet: #define USBD_NO_COPY 0x01 /* do not copy data to DMA buffer */ usbd_status usbd_open_pipe(usbd_interface_handle, u_int8_t, u_int8_t, usbd_pipe_handle *); usbd_xfer_handle usbd_alloc_xfer(usbd_device_handle); usbd_status usbd_close_pipe(usbd_pipe_handle); usb_endpoint_descriptor_t * usbd_interface2endpoint_descriptor(usbd_interface_handle iface, u_int8_t index); usbd_status usbd_device2interface_handle(usbd_device_handle, u_int8_t, usbd_interface_handle *); void usbd_get_xfer_status(usbd_xfer_handle, usbd_private_handle *, void **, u_int32_t *, usbd_status *); usbd_status usbd_abort_pipe(usbd_pipe_handle); void usbd_setup_isoc_xfer(usbd_xfer_handle, usbd_pipe_handle, usbd_private_handle, u_int16_t *, u_int32_t, u_int16_t, usbd_callback); void *usbd_alloc_buffer(usbd_xfer_handle, u_int32_t); usbd_status usbd_free_xfer(usbd_xfer_handle); usbd_status usbd_transfer(usbd_xfer_handle); pwcbsd/pwcbsd000755 000423 000000 00000000000 10553474346 014014 5ustar00luigiwheel000000 000000 pwcbsd/spca5xx-20060402000755 000423 000000 00000000000 10546676140 015016 5ustar00luigiwheel000000 000000 pwcbsd/qce-ga-0.40d000755 000423 000000 00000000000 10551677522 014411 5ustar00luigiwheel000000 000000 pwcbsd/qce-ga-0.40d/helper.h000644 000423 000000 00000000415 10546676143 016122 0ustar00luigiwheel000000 000000 #ifndef __LINUX_QUICKCAM_HELPER_H #define __LINUX_QUICKCAM_HELPER_H #define qcmax(a,b) ((a)>(b)?(a):(b)) #define qcmin(a,b) ((a)<(b)?(a):(b)) #define qcabs(a) ((a)>0?(a):-(a)) #define qcboundscheck(x,low,high) \ x = x > high ? high : (x < low ? low : x); #endif pwcbsd/qce-ga-0.40d/License000644 000423 000000 00000043131 10546676144 016002 0ustar00luigiwheel000000 000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. pwcbsd/qce-ga-0.40d/README000644 000423 000000 00000005121 10546676144 015352 0ustar00luigiwheel000000 000000 USB Quickcam Video Camera driver Supports Logitech Quickcam Express Quickcam Team: Lead developer Jean-Freceric Clere Nikolas Zimmermann Quickcam Revers-Enginnering: Georg Acher Photobit Support: Mark Cave-Ayland Would also like to thank the following contributors: Carlo E Prelz, Rogier Wolff, Samuel Linclau, Matthew Denner, and any body else whom we have failed to mention. Special thanks for the help for RB24 to YUV422 conversion: Aron Rosenberg Thank the guys, who wrote the cpia driver (P.Prengler,S.Bertin,J.Erdfelt) Lastly sourceforge for hosting the site Check http://qce-ga.sourceforge.net for updates/news/mailing lists Copyrights Major details at the beginning of quickcam.c, additional copyrighted material yuv.c(JF.Clere) and testquickcam.c(N.Zimmermann) see the beginnings of those files for detail. Logitech Quickcam Express USB Driver ------------------------------------ 1. How to compile? Just use a plain "make" to compile the driver. 2a. The easiest way to load modules is to use the quickcam.sh shell script To run it type ./quickcam.sh (If you have compiled USB and/or V4L support into Kernel rather than as modules you should use method 2b) 2b. How to use with insmod? Typing "insmod mod_quickcam.o" should do the trick. 3. What are qce-ga's features? * 2.2 + 2.4 Support * Video4Linux compatible driver * ProcFS Support (+ VideoProcFS) * RGB24 and YUV* support * full mmap() and read() support * .... 4. What are the parameters wich are accepted by mod_quickcam.o? Easily find it out with "modinfo -p mod_quickcam.o" insmod mod_quickcam.o debug=n (n=1,2,4,8,16,32 or any ored values). insmod mod_quickcam.o rgain=red bgain=blue ggain=green. red, blue and green are initial gain values this allows to correct the colour of the images. The default values are rgain=192 bgain=192 ggain=176 5. How to install the driver properly along with the other kernel modules: a. Unpack and compile the tarball as instructed above. Enter root mode: su - b. Copy the module to its proper place (the 2.4.20-2.2 will be replaced by your kernel version): cp mod_quickcam.o /lib/modules/2.4.20-2.2/kernel/drivers/usb/ c. In /etc/modules.conf, add the following line: alias camera mod_quickcam d. Run the command: /sbin/depmod -a e. Install the module by: modprobe camera ------------------------------------ Cheers, Nikolas Zimmermann pwcbsd/qce-ga-0.40d/hdcs.c000644 000423 000000 00000022621 10551677522 015560 0ustar00luigiwheel000000 000000 /* * qce-ga, linux V4L driver for the Quickcam Express and Dexxa Quickcam * * hdcs.c - HDCS Sensor Implementation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "quickcam.h" #include "hdcs.h" #include "helper.h" // static unsigned char control; // static unsigned char config; /* start grabbing */ static int hdcs_start(struct usb_device * dev, struct sensorctrl *sensor_ctrl) { struct quickcam_i2c i2cbuff; printf("hdcs start control %d\n", sensor_ctrl->control); usb_quickcam_i2c_new(&i2cbuff); usb_quickcam_i2c_add(&i2cbuff, sensor_ctrl->control,0x04); return(usb_quickcam_i2c_send(dev,&i2cbuff,HDCS_ADDR)); } /* stop grabbing */ static int hdcs_stop(struct usb_device * dev, struct sensorctrl *sensor_ctrl) { struct quickcam_i2c i2cbuff; usb_quickcam_i2c_new(&i2cbuff); usb_quickcam_i2c_add(&i2cbuff, sensor_ctrl->control,0x00); if (usb_quickcam_i2c_send(dev,&i2cbuff,HDCS_ADDR) < 0) return (-2); return(0); } /* sensor window to read out */ static int hdcs_set_size(struct usb_device *dev, int mode) { printf("hdcs_set_size mode %d\n", mode); if(mode != 0 && mode != 1) goto error; // 1: Y-full, 2: y-half if (usb_quickcam_set1(dev, STV_Y_CTRL, 1) < 0) goto error; // 06/0a : Half/Full if (usb_quickcam_set1(dev, STV_X_CTRL, 0x0a) < 0) goto error; return(0); error: return(-1); } /* * initialise parameters - copy + paste from Georg Acher's user module * for hdcs sensor. */ static int hdcs_init(struct usb_device *dev, int mode, int *rgain, int *bgain, int *ggain, struct sensorctrl *sensor_ctrl) { struct quickcam_i2c i2cbuff; short isosize; printf("hdcs_init\n"); if (*rgain<=0 || *rgain>255) *rgain=RGAIN_DEF; if (*bgain<=0 || *bgain>255) *bgain=BGAIN_DEF; if (*ggain<=0 || *ggain>255) *ggain=GGAIN_DEF; sensor_ctrl->mode=mode; if (mode) { sensor_ctrl->mode=2; // quater. sensor_ctrl->width = 176; sensor_ctrl->height = 144; } else { sensor_ctrl->width = 352; sensor_ctrl->height = 288; } if (usb_quickcam_set1(dev, STV_REG23, 0) < 0) goto error; /* set the STV0602AA in STV0600 emulation mode */ #if defined(__FreeBSD__) if (UGETW(dev->ddesc.idProduct)==0x0870) #else if (dev->descriptor.idProduct==0x0870) #endif if (usb_quickcam_set1(dev, 0x1446, 1) < 0) goto error; /* * reset the Image Sensor. (keeping it to 1 is a problem). */ usb_quickcam_i2c_new(&i2cbuff); usb_quickcam_i2c_add(&i2cbuff, sensor_ctrl->control,1); if (usb_quickcam_i2c_send(dev,&i2cbuff,HDCS_ADDR) < 0) { printk(KERN_ERR "usb_quickcam_i2c_send HDCS_CONTROL(0x01) failed\n"); goto error; } usb_quickcam_i2c_add(&i2cbuff,sensor_ctrl->control,0); if (usb_quickcam_i2c_send(dev,&i2cbuff,HDCS_ADDR) < 0) { printk(KERN_ERR "usb_quickcam_i2c_send HDCS_CONTROL(0x00) failed\n"); goto error; } // clear status. usb_quickcam_i2c_add(&i2cbuff,HDCS_STATUS, 0x00); // clear interrupt mask. usb_quickcam_i2c_add(&i2cbuff,HDCS_IMASK, 0x00); if (usb_quickcam_i2c_send(dev,&i2cbuff,HDCS_ADDR) < 0) goto error; /* was the place where Georg sets gains */ if (usb_quickcam_set1(dev, STV_REG00, 0x1d) < 0) goto error; if (usb_quickcam_set1(dev, STV_REG04, 0x07) < 0) goto error; if (usb_quickcam_set1(dev, STV_REG03, 0x95) < 0) goto error; if (usb_quickcam_set1(dev, STV_REG23, 0) < 0) goto error; // larger -> slower if (usb_quickcam_set1(dev, STV_SCAN_RATE, 0x20) <0) goto error; // ISO-Size, 0x34f = 847 .. 0x284 = 644 #if defined(__FreeBSD__) if (UGETW(dev->ddesc.idProduct)==0x0870) #else if (dev->descriptor.idProduct==0x0870) #endif isosize = 644; else isosize = 847; if (usb_quickcam_set2(dev, STV_ISO_SIZE, isosize) < 0) { printk(KERN_ERR "usb_quickcam_set2 STV_ISO_SIZE(847) failed\n"); goto error; } // Set Size if (hdcs_set_size(dev, mode) < 0) goto error; usb_quickcam_i2c_add(&i2cbuff,HDCS_STARTY, 2); usb_quickcam_i2c_add(&i2cbuff,HDCS_STARTX, 4); usb_quickcam_i2c_add(&i2cbuff,HDCS_STOPY, 0x4e); usb_quickcam_i2c_add(&i2cbuff,HDCS_STOPX, 0x5b); // 0x7-0x50 #if defined(__FreeBSD__) if (UGETW(dev->ddesc.idProduct)==0x0870) #else if (dev->descriptor.idProduct==0x0870) #endif usb_quickcam_i2c_add(&i2cbuff,HDCS_INTEGRATE, 0x7e); else usb_quickcam_i2c_add(&i2cbuff,HDCS_INTEGRATE, 0x09); usb_quickcam_i2c_add(&i2cbuff,sensor_ctrl->control, 0); usb_quickcam_i2c_add(&i2cbuff,HDCS_REG2A, 0x0); usb_quickcam_i2c_add(&i2cbuff,HDCS_REG2C, 0x0); if (usb_quickcam_i2c_send(dev,&i2cbuff,HDCS_ADDR) < 0) goto error; if (usb_quickcam_set1(dev, STV_REG01, 0xb5) < 0) goto error; if (usb_quickcam_set1(dev, STV_REG02, 0xa8) < 0) goto error; usb_quickcam_i2c_add(&i2cbuff,HDCS_REG06, 0x63); usb_quickcam_i2c_add(&i2cbuff,HDCS_REG08, 0x00); usb_quickcam_i2c_add(&i2cbuff,HDCS_REG0A, 0x20); usb_quickcam_i2c_add(&i2cbuff,HDCS_REG0C, 0x12); /* * CONFIG: 0x08 Continous Frame Capture. * 0x04 Stop when Frame Complete. */ usb_quickcam_i2c_add(&i2cbuff, sensor_ctrl->config, (mode?0x38:0x08)); usb_quickcam_i2c_add(&i2cbuff,HDCS_ADC_BITS, 10); if (usb_quickcam_i2c_send(dev,&i2cbuff,HDCS_ADDR) < 0) goto error; return(0); error: return(-1); } /* set_shutter */ static int hdcs_set_shutter(struct usb_device *dev, int sval, int xval, struct sensorctrl *sensor_ctrl) { struct quickcam_i2c i2cbuff; printf("hdcs_set_shutter s %d x %d\n", sval, xval); usb_quickcam_i2c_new(&i2cbuff); usb_quickcam_i2c_add(&i2cbuff, sensor_ctrl->control, 0); usb_quickcam_i2c_add(&i2cbuff,HDCS_SHUTTERL, sval&255); usb_quickcam_i2c_add(&i2cbuff,HDCS_SHUTTERH, sval>>8); usb_quickcam_i2c_add(&i2cbuff,HDCS_REG2A, xval&255); usb_quickcam_i2c_add(&i2cbuff,HDCS_REG2C, xval>>8); usb_quickcam_i2c_add(&i2cbuff, sensor_ctrl->control, 4); return(usb_quickcam_i2c_send(dev,&i2cbuff,HDCS_ADDR)); } /* * set gains * According to spec's 128 multiplies by 2 the values. */ static int hdcs_set_gains(struct usb_device *dev, int rgain, int bgain, int ggain) { struct quickcam_i2c i2cbuff; usb_quickcam_i2c_new(&i2cbuff); usb_quickcam_i2c_add(&i2cbuff,HDCS_GAIN_GREEN1, 128+qcmax(0,qcmin(127,ggain/2))); usb_quickcam_i2c_add(&i2cbuff,HDCS_GAIN_RED, 128+qcmax(0,qcmin(127,rgain/2))); usb_quickcam_i2c_add(&i2cbuff,HDCS_GAIN_BLUE, 128+qcmax(0,qcmin(127,bgain/2))); usb_quickcam_i2c_add(&i2cbuff,HDCS_GAIN_GREEN2, 128+qcmax(0,qcmin(127,ggain/2))); return(usb_quickcam_i2c_send(dev,&i2cbuff,HDCS_ADDR)); } /* * Sets the size (scaling) of the capture window. * If subsample could return the image size we use subsample. */ static int hdcs_set_window(struct usb_device *dev, int x, int y, int width, int height, struct sensorctrl *sensor_ctrl) { struct quickcam_i2c i2cbuff; usb_quickcam_i2c_new(&i2cbuff); if (!sensor_ctrl->mode) { sensor_ctrl->width=width; sensor_ctrl->height=height; if (width<=176 && height<=144) { width =width*2; height=height*2; usb_quickcam_i2c_add(&i2cbuff, sensor_ctrl->config, 0x38); } else usb_quickcam_i2c_add(&i2cbuff, sensor_ctrl->config, 0x08); } else { sensor_ctrl->width=width/2; sensor_ctrl->height=height/2; } usb_quickcam_i2c_add(&i2cbuff,HDCS_STARTY,2+y); usb_quickcam_i2c_add(&i2cbuff,HDCS_STARTX,4+x); usb_quickcam_i2c_add(&i2cbuff,HDCS_STOPY,(height+4)/4); // 4 usb_quickcam_i2c_add(&i2cbuff,HDCS_STOPX,(width+12)/4); // 12 return(usb_quickcam_i2c_send(dev,&i2cbuff,HDCS_ADDR)); } /* * Fill the pointers so that the routines are called from quickcam.c */ void load_hdcs_mod(struct sensorctrl *sensor_ctrl) { sensor_ctrl->init = hdcs_init; sensor_ctrl->set_shutter = hdcs_set_shutter; sensor_ctrl->set_gains = hdcs_set_gains; sensor_ctrl->set_window = hdcs_set_window; sensor_ctrl->set_size = hdcs_set_size; sensor_ctrl->start = hdcs_start; sensor_ctrl->stop = hdcs_stop; sensor_ctrl->control = HDCS_CONTROL; sensor_ctrl->config = HDCS_CONFIG; } void load_hdcs20_mod(struct sensorctrl *sensor_ctrl) { load_hdcs_mod(sensor_ctrl); sensor_ctrl->control = HDCS20_CONTROL; sensor_ctrl->config = HDCS20_CONFIG; } pwcbsd/qce-ga-0.40d/hdcs.h000644 000423 000000 00000002322 10551677522 015561 0ustar00luigiwheel000000 000000 #ifndef __LINUX_QUICKCAM_HDCS_H #define __LINUX_QUICKCAM_HDCS_H void load_hdcs_mod(struct sensorctrl *sensor_ctrl); void load_hdcs20_mod(struct sensorctrl *sensor_ctrl); // I2C Address #define HDCS_ADDR 0xaa // I2C Registers #define HDCS_IDENT 0x00 #define HDCS_STATUS 0x02 // Status #define HDCS_IMASK 0x04 // Interrupt mask #define HDCS_REG06 0x06 #define HDCS_REG08 0x08 #define HDCS_REG0A 0x0a #define HDCS_REG0C 0x0c #define HDCS_REG0E 0x0e #define HDCS_REG10 0x10 #define HDCS_ADC_BITS 0x12 #define HDCS_STARTY 0x14 #define HDCS_STARTX 0x16 #define HDCS_STOPY 0x18 #define HDCS_STOPX 0x1a #define HDCS_INTEGRATE 0x1c #define HDCS_GAIN_GREEN1 0x1e #define HDCS_GAIN_RED 0x20 #define HDCS_GAIN_BLUE 0x22 #define HDCS_GAIN_GREEN2 0x24 #define HDCS_SHUTTERL 0x26 #define HDCS_SHUTTERH 0x28 #define HDCS_REG2A 0x2a #define HDCS_REG2C 0x2C #define HDCS_CONFIG 0x2E // Configuration #define HDCS20_CONFIG 0x36 // Configuration for HDCS1020 #define HDCS_CONTROL 0x30 // Control #define HDCS20_CONTROL 0x38 // Control for HDCS1020 #define HDCS_REG32 0x32 #define HDCS_REG34 0x34 #define HDCS_REG36 0x36 #define HDCS_REG38 0x38 #endif pwcbsd/qce-ga-0.40d/testquickcam000755 000423 000000 00000000000 10546676144 017111 5ustar00luigiwheel000000 000000 pwcbsd/qce-ga-0.40d/makefile000644 000423 000000 00000001725 10546676144 016200 0ustar00luigiwheel000000 000000 LINUX_DIR = /lib/modules/$(shell uname -r)/build INSTALL = /usr/bin/install RELEASE = $(shell uname -r) MODULE_INC=-I$(LINUX_DIR)/drivers/usb -I$(LINUX_DIR)/include -include $(LINUX_DIR)/include/linux/config.h MODULE_DEFS:=-DMODULE -D__KERNEL__ $(shell [ -f $(LINUX_DIR)/include/linux/modversions.h ] && echo -DEXPORT_SYMTAB -DMODVERSIONS -include $(LINUX_DIR)/include/linux/modversions.h ) MODULE_OPT = -O3 MODULE_OPT_WARN = -Wall -Wstrict-prototypes -fomit-frame-pointer -pipe MODULE_CFLAGS = $(MODULE_INC) $(DEBUG) $(MODULE_OPT) $(MODULE_OPT_WARN) $(MODULE_DEFS) all: mod_quickcam.o clean: rm *.o -f mod_quickcam.o: quickcam.o hdcs.o pb0100.o yuv.o vv6410.o memory.o ld -r -o mod_quickcam.o quickcam.o hdcs.o pb0100.o yuv.o vv6410.o memory.o quickcam.o: quickcam.c quickcam.h pb0100.h hdcs.h vv6410.h memory.h .c.o: $(CC) $(MODULE_CFLAGS) -c $< install: mod_quickcam.o $(INSTALL) -D -c -m 644 mod_quickcam.o /lib/modules/$(RELEASE)/misc/mod_quickcam.o depmod -a pwcbsd/qce-ga-0.40d/makefile.inter000644 000423 000000 00000001342 10546676144 017313 0ustar00luigiwheel000000 000000 LINUX_DIR = /usr/src/linux DRIVER_INC=-I$(LINUX_DIR)/drivers/usb -I$(LINUX_DIR)/include -include $(LINUX_DIR)/include/linux/config.h DRIVER_DEFS:=-D__KERNEL__ $(shell [ -f /usr/include/linux/modversions.h ] && echo -DEXPORT_SYMTAB -DMODVERSIONS -include /usr/include/linux/modversions.h ) DRIVER_OPT = -O3 DRIVER_OPT_WARN = -Wall -Wstrict-prototypes -fomit-frame-pointer -pipe DRIVER_CFLAGS = $(DRIVER_INC) $(DEBUG) $(DRIVER_OPT) $(DRIVER_OPT_WARN) $(DRIVER_DEFS) all: kernel_quickcam.o clean: rm *.o -f kernel_quickcam.o: quickcam.o hdcs.o pb0100.o yuv.o vv6410.o ld -r -o kernel_quickcam.o quickcam.o hdcs.o pb0100.o yuv.o vv6410.o quickcam.o: quickcam.c quickcam.h pb0100.h hdcs.h vv6410.h .c.o: $(CC) $(DRIVER_CFLAGS) -c $< pwcbsd/qce-ga-0.40d/memory.c000644 000423 000000 00000007004 10546676144 016150 0ustar00luigiwheel000000 000000 /* * qce-ga, linux V4L driver for the Quickcam Express and Dexxa Quickcam * * memory.c - contains all needed memory management functions * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include /* Required on Alpha, from Bob McElrath */ #include /* Required on Alpha */ #include /* Required on Alpha */ #include /* pmd_offset requires this on SuSE supplied kernels */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) #define MAP_NR virt_to_page #endif /* * Given PGD from the address space's page table, return the kernel * virtual mapping of the physical memory mapped at ADR. */ inline unsigned long uvirt_to_kva(pgd_t *pgd, unsigned long adr) { unsigned long ret = 0UL; pmd_t *pmd; pte_t *ptep, pte; if(!pgd_none(*pgd)) { pmd = pmd_offset(pgd, adr); if(!pmd_none(*pmd)) { #ifdef pte_offset /* Check if it is not a kernel using the new rmap-vm */ ptep = pte_offset(pmd, adr); #else ptep = pte_offset_map(pmd, adr); #endif pte = *ptep; if(pte_present(pte)) { ret = (unsigned long) page_address(pte_page(pte)); ret |= (adr & (PAGE_SIZE - 1)); } } } return ret; } inline unsigned long uvirt_to_bus(unsigned long adr) { unsigned long kva, ret; kva = uvirt_to_kva(pgd_offset(current->mm, adr), adr); ret = virt_to_bus((void *) kva); return ret; } inline unsigned long kvirt_to_bus(unsigned long adr) { unsigned long va, kva, ret; va = VMALLOC_VMADDR(adr); kva = uvirt_to_kva(pgd_offset_k(va), va); ret = virt_to_bus((void *) kva); return ret; } /* * Here we want the physical address of the memory. * This is used when initializing the contents of the * area and marking the pages as reserved. */ inline unsigned long kvirt_to_pa(unsigned long adr) { unsigned long va, kva, ret; va = VMALLOC_VMADDR(adr); kva = uvirt_to_kva(pgd_offset_k(va), va); ret = __pa(kva); return ret; } void *rvmalloc(unsigned long size) { void *mem; unsigned long adr, page; /* Round it off to PAGE_SIZE */ size += (PAGE_SIZE - 1); size &= ~(PAGE_SIZE - 1); mem = vmalloc(size); if(!mem) return NULL; memset(mem, 0, size); /* Clear the ram out, no junk to the user */ adr = (unsigned long) mem; while(size > 0) { page = kvirt_to_pa(adr); mem_map_reserve(MAP_NR(__va(page))); adr += PAGE_SIZE; if(size > PAGE_SIZE) size -= PAGE_SIZE; else size = 0; } return mem; } void rvfree(void *mem, unsigned long size) { unsigned long adr, page; if(!mem) return; size += (PAGE_SIZE - 1); size &= ~(PAGE_SIZE - 1); adr = (unsigned long) mem; while(size > 0) { page = kvirt_to_pa(adr); mem_map_unreserve(MAP_NR(__va(page))); adr += PAGE_SIZE; if(size > PAGE_SIZE) size -= PAGE_SIZE; else size = 0; } vfree(mem); } pwcbsd/qce-ga-0.40d/memory.h000644 000423 000000 00000000641 10546676144 016155 0ustar00luigiwheel000000 000000 #ifndef __LINUX_QUICKCAM_MEMORY_H #define __LINUX_QUICKCAM_MEMORY_H #if !defined(__FreeBSD__) inline unsigned long uvirt_to_kva(pgd_t *pgd, unsigned long adr); inline unsigned long uvirt_to_bus(unsigned long adr); inline unsigned long kvirt_to_bus(unsigned long adr); inline unsigned long kvirt_to_pa(unsigned long adr); void *rvmalloc(unsigned long size); void rvfree(void *mem, unsigned long size); #endif #endif pwcbsd/qce-ga-0.40d/pb0100.c000644 000423 000000 00000017335 10551677522 015547 0ustar00luigiwheel000000 000000 /* * qce-ga, linux V4L driver for the Quickcam Express and Dexxa Quickcam * * pb0100.c - PB0100 Sensor Implementation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "quickcam.h" #include "pb0100.h" static int pb0100_set_size(struct usb_device * dev, int mode) { if(mode != 0 && mode != 1) goto error; // Set screen output size if (usb_quickcam_set1(dev, STV_Y_CTRL, (mode?2:1))<0) // 1: Y-full, 2: y-half goto error; if (usb_quickcam_set1(dev, STV_X_CTRL, (mode?6:0x0a))<0) // 06/0a : Half/Full goto error; return(0); error: return(-1); } /* * initialise parameters of PB100 sensor. */ static int pb0100_init(struct usb_device *dev, int mode, int *rgain, int *bgain, int *ggain, struct sensorctrl *sensor_ctrl) { struct quickcam_i2c i2cbuff; usb_quickcam_i2c_new(&i2cbuff); if (*rgain<=0 || *rgain>255) *rgain=RGAIN_DEF; if (*bgain<=0 || *bgain>255) *bgain=BGAIN_DEF; if (*ggain<=0 || *ggain>255) *ggain=GGAIN_DEF; sensor_ctrl->mode=mode; if (mode) { sensor_ctrl->width = 176; sensor_ctrl->height = 144; } else { sensor_ctrl->width = 352; sensor_ctrl->height = 288; } if (usb_quickcam_set1(dev, STV_REG00, 1)<0) goto error; if (usb_quickcam_set1(dev, STV_SCAN_RATE, 0)<0) goto error; // Reset sensor usb_quickcam_i2c_add2(&i2cbuff, PB_RESET, 1); if (usb_quickcam_i2c_send(dev,&i2cbuff,PB_ADDR)<0) goto error; usb_quickcam_i2c_add2(&i2cbuff, PB_RESET, 0); if (usb_quickcam_i2c_send(dev,&i2cbuff,PB_ADDR)<0) goto error; // Disable chip usb_quickcam_i2c_add2(&i2cbuff, PB_CONTROL, 0x28); if (usb_quickcam_i2c_send(dev,&i2cbuff,PB_ADDR)<0) goto error; // Gain stuff... usb_quickcam_i2c_add2(&i2cbuff, PB_PREADCTRL, 0x1440); usb_quickcam_i2c_add2(&i2cbuff, PB_ADCMAXGAIN, 0x7F); /* gain max */ usb_quickcam_i2c_add2(&i2cbuff, PB_ADCMINGAIN, 1); /* gain min */ if (usb_quickcam_i2c_send(dev,&i2cbuff,PB_ADDR)<0) goto error; // Enable auto-exposure... usb_quickcam_i2c_add2(&i2cbuff, PB_EXPGAIN, 17); usb_quickcam_i2c_add2(&i2cbuff, PB_UPDATEINT, 1); usb_quickcam_i2c_add2(&i2cbuff, PB_VOFFSET, 0); /* 0x14 */ usb_quickcam_i2c_add2(&i2cbuff, PB_ADCGAINH, 0x38); /* 0xd */ usb_quickcam_i2c_add2(&i2cbuff, PB_ADCGAINL, 0x0); /* 0x1 */ if (usb_quickcam_i2c_send(dev,&i2cbuff,PB_ADDR)<0) goto error; // Set individual colour gains usb_quickcam_i2c_add2(&i2cbuff, PB_RGAIN, *rgain); usb_quickcam_i2c_add2(&i2cbuff, PB_G1GAIN, *ggain); usb_quickcam_i2c_add2(&i2cbuff, PB_G2GAIN, *ggain); usb_quickcam_i2c_add2(&i2cbuff, PB_BGAIN, *bgain); if (usb_quickcam_i2c_send(dev,&i2cbuff,PB_ADDR)<0) goto error; // ??? if (usb_quickcam_set1(dev, STV_REG04, 0x07)<0) goto error; if (usb_quickcam_set1(dev, STV_REG03, 0x45)<0) goto error; if (usb_quickcam_set1(dev, STV_REG00, 0x11)<0) goto error; // Set screen output size if (pb0100_set_size(dev, mode) <0) goto error; // 0x27b: 635... why? - HDCS uses 847 - if (usb_quickcam_set2(dev, STV_ISO_SIZE, 847)<0) // ISO-Size goto error; // Setup sensor window usb_quickcam_i2c_add2(&i2cbuff, PB_RSTART, 0); usb_quickcam_i2c_add2(&i2cbuff, PB_CSTART, 0); usb_quickcam_i2c_add2(&i2cbuff, PB_RWSIZE, 0x11f); // 0xf7: 240 usb_quickcam_i2c_add2(&i2cbuff, PB_CWSIZE, 0x15f); // 0x13f: 320 if (usb_quickcam_i2c_send(dev,&i2cbuff,PB_ADDR)<0) goto error; // Scan rate? if (usb_quickcam_set1(dev, STV_SCAN_RATE, (mode?0x10:0x20))<0) // larger -> slower goto error; // Scan/timing for the sensor usb_quickcam_i2c_add2(&i2cbuff, PB_ROWSPEED, 0x1a); usb_quickcam_i2c_add2(&i2cbuff, PB_CFILLIN, 0x2f); usb_quickcam_i2c_add2(&i2cbuff, PB_VBL, 0); usb_quickcam_i2c_add2(&i2cbuff, PB_FINTTIME, 0); usb_quickcam_i2c_add2(&i2cbuff, PB_RINTTIME, 0x7b); if (usb_quickcam_i2c_send(dev,&i2cbuff,PB_ADDR)<0) goto error; if (usb_quickcam_set1(dev, STV_REG01, 0xc2)<0) goto error; if (usb_quickcam_set1(dev, STV_REG02, 0xb0)<0) goto error; return(0); error: return(-1); } static int pb0100_set_shutter(struct usb_device * dev, int sval, int xval, struct sensorctrl *sensor_ctrl) { return(0); } static int pb0100_set_gains(struct usb_device * dev, int rgain, int bgain, int ggain) { return(0); } /* Window location and size are controlled by R1, R2, R3 and R4. * The default size is CIF (352x288) with to right at (4,12) * and bottom left at (355, 299) * * We try to ensure that the captured area is in the center of * the camera purely because that's nicer. It would be better * if the PB0100 sensor supported capture scaling! * * We do it in on step otherwise size changemay take more * than one frame (like xawtv who tests 64x48 and uses 352x288) * 3072 = 64x48, 16896 = 352x48, 101376 = 352x288. */ static int pb0100_set_window(struct usb_device *dev, int x, int y, int w, int h, struct sensorctrl *sensor_ctrl) { struct quickcam_i2c i2cbuff; usb_quickcam_i2c_new(&i2cbuff); /* PB_RSTART = 12 + y */ usb_quickcam_i2c_add2(&i2cbuff,PB_RSTART,12 + y); /* PB_CSTART = 4 + x */ usb_quickcam_i2c_add2(&i2cbuff,PB_CSTART,4 + x); /* PB_RWSIZE = h - 1 */ usb_quickcam_i2c_add2(&i2cbuff,PB_RWSIZE,h - 1); /* PB_CWSIZE = w - 1 */ usb_quickcam_i2c_add2(&i2cbuff,PB_CWSIZE, w - 1); if (sensor_ctrl->mode) { sensor_ctrl->width=w/2; sensor_ctrl->height=h/2; } else { sensor_ctrl->width=w; sensor_ctrl->height=h; } return(usb_quickcam_i2c_send(dev,&i2cbuff,PB_ADDR)); } /* start grabbing */ static int pb0100_start(struct usb_device * dev, struct sensorctrl *sensor_ctrl) { struct quickcam_i2c i2cbuff; usb_quickcam_i2c_new(&i2cbuff); usb_quickcam_i2c_add2(&i2cbuff,PB_CONTROL, 0x2b); return(usb_quickcam_i2c_send(dev,&i2cbuff,PB_ADDR)); } /* stop grabbing */ static int pb0100_stop(struct usb_device * dev, struct sensorctrl *sensor_ctrl) { struct quickcam_i2c i2cbuff; usb_quickcam_i2c_new(&i2cbuff); usb_quickcam_i2c_add2(&i2cbuff,PB_ABORTFRAME, 1); if (usb_quickcam_i2c_send(dev,&i2cbuff,PB_ADDR)<0) return (-3); usb_quickcam_i2c_add2(&i2cbuff,PB_CONTROL, 0x28); if (usb_quickcam_i2c_send(dev,&i2cbuff,PB_ADDR)<0) return (-2); return(0); } /* * Fill the pointers so that the routines are called from quickcam.c */ void load_pb0100_mod(struct sensorctrl *sensor_ctrl) { sensor_ctrl->init = pb0100_init; sensor_ctrl->set_shutter = pb0100_set_shutter; sensor_ctrl->set_gains = pb0100_set_gains; sensor_ctrl->set_window = pb0100_set_window; sensor_ctrl->set_size = pb0100_set_size; sensor_ctrl->start = pb0100_start; sensor_ctrl->stop = pb0100_stop; sensor_ctrl->width = 176; sensor_ctrl->height = 144; sensor_ctrl->mode = 1; } pwcbsd/qce-ga-0.40d/pb0100.h000644 000423 000000 00000004104 10551677522 015542 0ustar00luigiwheel000000 000000 #ifndef __LINUX_QUICKCAM_PB0100_H #define __LINUX_QUICKCAM_PB0100_H void load_pb0100_mod(struct sensorctrl *sensor_ctrl); // I2C Address #define PB_ADDR 0xba // I2C Registers #define PB_IDENT 0 // Identification #define PB_RSTART 1 // First row #define PB_CSTART 2 // First column #define PB_RWSIZE 3 // Row window size #define PB_CWSIZE 4 // Col window size #define PB_CFILLIN 5 // Col fill-in #define PB_VBL 6 // Vertical blank count #define PB_CONTROL 7 // Control Mode #define PB_FINTTIME 8 // Frame integration time #define PB_RINTTIME 9 // Row frame integration time #define PB_ROWSPEED 10 // Row speed control #define PB_ABORTFRAME 11 // Abort Frame. #define PB_RESET 13 // Hard reset. #define PB_EXPGAIN 14 // Exposure Gain Command. #define PB_UPDATEINT 23 // Auto-exposure update interval. #define PB_PREADCTRL 32 // Pixel Read Control Mode. #define PB_G1GAIN 43 // Green gain #define PB_BGAIN 44 // Blue gain #define PB_RGAIN 45 // Red gain. #define PB_G2GAIN 46 // Greeb gain (one line is RG the other BG). #define PB_ADCMAXGAIN 51 // Max gain for auto-exposure. #define PB_ADCMINGAIN 52 // Min gain for auto-exposure. #define PB_ADCGLOBALGAIN 53 // Global gain (copied in G1,G2,B and R). #define PB_VOFFSET 57 // Voltage offset. #define PB_ADCGAINH 59 // high ref. #define PB_ADCGAINL 60 // low ref. /* * The spec file for the PB-0100 suggests the following for best quality * images after the sensor has been reset : * * R60 = 0x03 (3 dec) : sets low reference of ADC to produce good black level * R32 = 0x1400 (5120 dec) : Enables global gain changes through R53 * R52 = 0x10 (16 dec) : Sets the minimum gain for auto-exposure * R53 = 0x10 (16 dec) : Sets the global gain * R14 = 0x11 (17 dec) : Sets the auto-exposure value * R23 = 0x02 (2 dec) : Sets the speed on auto-exposure routine * R5 = 0x0e (14 dec) : Sets the frame rate */ #endif pwcbsd/qce-ga-0.40d/quickcam.c000644 000423 000000 00000202410 10551677522 016430 0ustar00luigiwheel000000 000000 /* * qce-ga, linux V4L driver for the Quickcam Express and Dexxa Quickcam * * quickcam.c - main driver part * * Copyright (C) 2001 Jean-Fredric Clere,Nikolas Zimmermann, Georg Acher * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ /* Cam variations of Logitech Quickcam Express: P/N 861037: Sensor HDCS1000 ASIC STV0600 P/N 861050-0010: Sensor HDCS1000 ASIC STV0600 P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600 P/N 861055: Sensor ST VV6410 ASIC STV0610 ("LEGO cam" not _yet_ supported) P/N 861075-0040: Sensor HDCS1000 ASIC P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 (Dexxa WebCam USB not _yet_ supported) For any questions ask qce-ga-devel@lists.sourceforge.net! */ #if !defined(__FreeBSD__) #include #include #include #include #include #include #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) #include #else #include #endif #include #include #include #include #include #include #include #include #include #include #include #endif /* ! __FreeBSD__ */ #include "quickcam.h" #include "pb0100.h" #include "hdcs.h" #include "vv6410.h" #include "memory.h" #include "helper.h" #if !defined(__FreeBSD__) #ifndef MODULE #define DEBUGLEVEL 0 // put in your wanted value here #endif #ifdef MODULE #define DEBUGLEVEL 0 MODULE_PARM(debug, "i"); MODULE_PARM(interpolation, "i"); MODULE_PARM(mode, "i"); MODULE_PARM(keepexposure, "i"); MODULE_PARM(tobgr, "i"); MODULE_PARM(rgain, "i"); MODULE_PARM(bgain, "i"); MODULE_PARM(ggain, "i"); MODULE_PARM(bright,"i"); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,5) MODULE_PARM(video_nr,"i"); #endif MODULE_PARM_DESC(debug, "Sets the debug output (1,2,4,8,16,32)"); MODULE_PARM_DESC(interpolation, "Sets the interpolation mode (0-1)"); MODULE_PARM_DESC(mode, "Sets the speed (0-1)"); MODULE_PARM_DESC(keepexposure, "Keep gain settings across one open to another (0-1)"); MODULE_PARM_DESC(tobgr, "Automatic RGB -> BGR conversion"); MODULE_PARM_DESC(rgain, "Initial value of red gain (0-255)"); MODULE_PARM_DESC(bgain, "Initial value of blue gain (0-255)"); MODULE_PARM_DESC(ggain, "Initial value of green gains (0-255)"); MODULE_PARM_DESC(bright,"Initial value for brightness (0-65535)"); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,5) MODULE_PARM_DESC(video_nr, "Set videodevice number (/dev/videoX)"); #endif MODULE_SUPPORTED_DEVICE("video"); MODULE_DESCRIPTION("Logitech Quickcam Express Webcam driver"); MODULE_AUTHOR("see README"); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,9) #ifdef MODULE_LICENSE MODULE_LICENSE("GPL"); #endif #endif #endif static int debug = DEBUGLEVEL; static int keepexposure = 0; static int tobgr = 0; static int interpolation = 1; /* 1 = bilinear interpolation */ static int mode = 0; /* normal or sub-sample (sub-sample to increase the speed) */ /* gains allow the user decide the initial value of the gains */ static int rgain = 0; static int bgain = 0; static int ggain = 0; // static int bright= 32768; /* Zero gives black images with gnomemeeting */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,5) /* video_nr option allows to specify a certain /dev/videoX device */ /* (like /dev/video0 or /dev/video1 ...) */ /* for autodetect first available use video_nr=-1 (defaultvalue) */ /* (code reused from bttv driver http://bytesex.org/bttv/) */ // static int video_nr = -1; #endif /* Video Size 352 x 288 x 4 bytes for 0RGB 32 bpp mode */ #define MAX_FRAME_SIZE (352 * 288 * 4) #define VERSION "$Id: quickcam.c,v 1.12 2007/01/08 17:41:48 luigi Exp $" static struct usb_driver quickcam_driver; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) static __devinitdata struct usb_device_id device_table [] = { { USB_DEVICE(0x046d, 0x0840) }, /* Quickcam Express */ { USB_DEVICE(0x046d, 0x0850) }, /* LEGO cam - not yet supported */ { USB_DEVICE(0x046d, 0x0870) }, /* Dexxa webcam USB */ { } }; MODULE_DEVICE_TABLE (usb, device_table); #endif // LINUX_VERSION... static struct sensor_data sensors [] = { /* name reg23 i2c_addr id_reg id l load */ /* ---- ----- ----------- -------------- ---- - -------------- */ { "HDCS1000", 0, HDCS_ADDR, HDCS_IDENT + 1, 0x08, 1, load_hdcs_mod }, { "BP100", 1, PB_ADDR, PB_IDENT, 0x64, 2, load_pb0100_mod }, { "VV6410", 5, VV6410_ADDR, VV6410_IDENT, 0x19, 1, load_vv6410_mod }, { "HDCS1020", 0, HDCS_ADDR, HDCS_IDENT + 1, 0x10, 1, load_hdcs20_mod }, { NULL } }; /* * HexDump a string... */ static void usbvideo_HexDump(const unsigned char *data, int len) { const int bytes_per_line = 32; char tmp[128]; /* 32*3 + 5 */ int i, k; for (i=k=0; len > 0; i++, len--) { if (i > 0 && ((i % bytes_per_line) == 0)) { printk("%s\n", tmp); k=0; } if ((i % bytes_per_line) == 0) k += sprintf(&tmp[k], "[%04x]: ", i); k += sprintf(&tmp[k], "%02x ", data[i]); } if (k > 0) printk("%s\n", tmp); } /* * /proc interface for our driver */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) #if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS) static struct proc_dir_entry *quickcam_proc_entry = NULL; extern struct proc_dir_entry *video_proc_entry; #define CHECK(x) ((x) ? "Yes" : "No") #define CHOOSE(x,y,a,b) ((x == y) ? a : b) static int quickcam_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) { char *out = page; int len; struct usb_quickcam *dev = data; out += sprintf(out, "*** Driver Status ***\n"); out += sprintf(out, "Driver Version : %s\n", VERSION); out += sprintf(out, "Sensor : %s\n", dev->sensor_name); out += sprintf(out, "Streaming : %s\n", CHECK(dev->streaming)); out += sprintf(out, "Grabbing : %s\n", CHECK(dev->grabbing)); out += sprintf(out, "Reading frame : %d\n", dev->readframe); out += sprintf(out, "\n*** Sensor Control ***\n"); out += sprintf(out, "Shutter value : %d\n", dev->shutter_val); out += sprintf(out, "Gain : %d\n", dev->gain); out += sprintf(out, "Exposure value : %ld\n", dev->val); out += sprintf(out, "Red Gain : %d\n", dev->red); out += sprintf(out, "Green Gain : %d\n", dev->green); out += sprintf(out, "Blue Gain : %d\n", dev->blue); out += sprintf(out, "\n*** Output Window ***\n"); out += sprintf(out, "Width : %d\n", dev->vwin.width); out += sprintf(out, "Height : %d\n", dev->vwin.height); out += sprintf(out, "\n*** Output Picture ***\n"); out += sprintf(out, "Brightness : %d\n", dev->vpic.brightness); out += sprintf(out, "Whiteness : %d\n", dev->vpic.whiteness); out += sprintf(out, "Contrast : %d\n", dev->vpic.contrast); out += sprintf(out, "Hue : %d\n", dev->vpic.hue); out += sprintf(out, "Color : %d\n", dev->vpic.colour); out += sprintf(out, "Palette : %s\n", FormatName(dev->vpic.palette)); len = out - page; len -= off; if(len < count) { *eof = 1; if(len <= 0) return 0; } else len = count; *start = page + off; return len; } static int quickcam_write_proc(struct file *file, const char *buffer, unsigned long count, void *data) { // we don't support this....yet? return -EINVAL; } static void create_proc_quickcam(struct usb_quickcam *dev) { char name[9]; struct proc_dir_entry *entry; if(!quickcam_proc_entry || !dev) return; sprintf(name, "video%d", dev->vdev.minor); entry = create_proc_entry(name, S_IFREG | S_IRUGO | S_IWUSR, quickcam_proc_entry); if(!entry) return; entry->data = dev; entry->read_proc = quickcam_read_proc; entry->write_proc = quickcam_write_proc; dev->proc_entry = entry; } static void destroy_proc_quickcam(struct usb_quickcam *dev) { char name[9]; if(!dev->proc_entry || !dev) return; sprintf(name, "video%d", dev->vdev.minor); remove_proc_entry(name, quickcam_proc_entry); dev->proc_entry = NULL; } static void proc_quickcam_create(void) { if(!video_proc_entry) return; quickcam_proc_entry = create_proc_entry("quickcam", S_IFDIR, video_proc_entry); if(quickcam_proc_entry) quickcam_proc_entry->owner = THIS_MODULE; } static void proc_quickcam_destroy(void) { if(!quickcam_proc_entry) return; remove_proc_entry("quickcam", video_proc_entry); } #endif #endif // LINUX_VERSION... /* * I2C read registers of HDCS1000, the result will be in the STV0600 register 0x1410. */ static int usb_quickcam_i2c_in(struct usb_device *dev, int reg, unsigned char sensor_addr) { char buff[35]; /* why 35 = 23 hex? */ buff[0]=reg; buff[0x20]=sensor_addr; buff[0x21]=0; // 1 value buff[0x22]=3; // Read cmd. return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 0x04, 0x40, /* UT_WRITE | UT_VENDOR | UT_DEVICE */ 0x1400, 0, buff, 0x23 , HZ); } /* read one byte identification register for HDCS. * write command to sensor. * read the STV0600. */ int usb_quickcam_get_i2c(struct usb_device *dev, unsigned char sensor_addr, int reg, void *buf, int len) { if (usb_quickcam_i2c_in(dev,reg,sensor_addr)<0) { printk("usb_quickcam_i2c_in failed\n"); return(-1); } return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), 0x04, 0xC0, /* UT_READ | UT_VENDOR | UT_DEVICE */ 0x1410, 0, buf, len, HZ); } /* * Set register one byte */ int usb_quickcam_set1(struct usb_device *dev, short reg, char val) { char buff[1]; buff[0] = val; return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 0x04, 0x40, reg, 0, buff, 1, HZ); } /* * Set register two byte */ int usb_quickcam_set2(struct usb_device *dev, short reg, short val) { char buff[2]; buff[0] = val&0xFF; buff[1] = (val>>8)&255; return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 0x04, 0x40, reg, 0, buff, 2, HZ); } /* Send a command to the sensor */ static int quickcam_usb_control_msg(struct usb_device *dev, char *buff) { return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 0x04, 0x40, 0x400, 0, buff, 0x23 , HZ); } /** * Clean buffer for I2C messages. */ void usb_quickcam_i2c_new(struct quickcam_i2c *i2cbuff) { i2cbuff->length=0; memset(i2cbuff->buff,'\0',0x23); } /** * Add register and byte value. */ void usb_quickcam_i2c_add(struct quickcam_i2c *i2cbuff, unsigned char reg, unsigned char value) { i2cbuff->buff[i2cbuff->length] = reg; i2cbuff->buff[i2cbuff->length+0x10] = value; i2cbuff->length++; } /** * Add register and 2 bytes value. */ void usb_quickcam_i2c_add2(struct quickcam_i2c *i2cbuff, unsigned char reg, unsigned short value) { i2cbuff->buff[i2cbuff->length] = reg; i2cbuff->buff[(i2cbuff->length*2)+0x10] = value&255; i2cbuff->buff[(i2cbuff->length*2)+0x11] = (value>>8)&255; i2cbuff->length++; } /** * Send the I2C message. */ int usb_quickcam_i2c_send(struct usb_device *dev, struct quickcam_i2c *i2cbuff, unsigned char sensor_add) { int ret; i2cbuff->buff[0x20]=sensor_add; i2cbuff->buff[0x21]=i2cbuff->length-1; i2cbuff->buff[0x22]=1; // Write cmd, 03 would be read. ret = quickcam_usb_control_msg(dev,i2cbuff->buff); usb_quickcam_i2c_new(i2cbuff); return(ret); } /* set the gains according to V4L default settings */ static int usb_quickcam_set_gains(struct usb_quickcam *quickcam) { struct usb_device *dev = quickcam->dev; int gr, gb, gg; gr=qcmin(255,qcmax(1,(quickcam->gain*quickcam->red)>>7)); gb=qcmin(255,qcmax(1,(quickcam->gain*quickcam->blue)>>7)); gg=qcmin(255,qcmax(1,(quickcam->gain*quickcam->green)>>7)); if (debug&DEBUGDATA) { printk("usb_quickcam_set_gains: gain %d, red %d, blue %d, green %d\n", quickcam->gain,quickcam->red,quickcam->blue,quickcam->green); printk("usb_quickcam_set_gains: gr %d, gb %d, gg %d\n",gr,gb,gg); } return quickcam->sensor_ctrl.set_gains(dev,gr,gb,gg); } /* * Set the exposure: note the val is a long, kernel does not know __divdi3, * it is in libc but it a big piece of code... * Of this is a copy ;=) of Georg Acher (acher@in.tum.de) set_exposure(). * And he says: "Auto-Exposure needs a bit more intelligence"! */ static int usb_quickcam_set_exposure(struct usb_quickcam *quickcam, long val) { int og=quickcam->gain; int os=quickcam->shutter_val; int brightness=quickcam->brightness>>8; struct usb_device *dev = quickcam->dev; quickcam->val = val; //printk("usb_quickcam_set_exposure: required %d, measured %ld\n",brightness,val); if (qcabs(val-brightness)>5) quickcam->gain-=(val-brightness)/5; quickcam->gain=qcmin(255,qcmax(1,quickcam->gain)); /* It seems that the less gain you have the better ... You have less noise */ if (quickcam->gain<=2 && quickcam->shutter_val>2) { quickcam->gain+=2; quickcam->shutter_val-=2; } /* Instead of amplifying the signal, open the shutter to get more light ! */ if (quickcam->gain>=200 && quickcam->shutter_val<254) { quickcam->gain-=2; quickcam->shutter_val+=2; } if (debug&DEBUGDATA) { printk("usb_quickcam_set_exposure: required %d, mesured %ld\n",brightness,val); printk("usb_quickcam_set_exposure: gain %d shutter %d\n",quickcam->gain,quickcam->shutter_val); } if (os!=quickcam->shutter_val) quickcam->sensor_ctrl.set_shutter(dev,quickcam->shutter_val,0x100, &quickcam->sensor_ctrl); if (og!=quickcam->gain) return usb_quickcam_set_gains(quickcam); return 0; } /* Stop camera and disable ISO-streaming */ int usb_quickcam_stop(struct usb_quickcam *quickcam) { int ret=0; if (!quickcam->dev) return 0; // nothing to do. // stop ISO-streaming. if (usb_quickcam_set1(quickcam->dev, STV_ISO_ENABLE, 0)<0) ret = -1; // stop current frame. ret = quickcam->sensor_ctrl.stop(quickcam->dev, &(quickcam->sensor_ctrl)); if (debug&DEBUGLOGIC) printk("usb_quickcam_stop:%d\n",ret); return ret; } /* * Change the control register to start an image capture. */ int usb_quickcam_upload_frame(struct usb_quickcam *quickcam) { if (debug&DEBUGLOGIC) printk("usb_quickcam_upload_frame %d\n",quickcam->scanstate); #if 0 if (quickcam->scanstate==STATE_ERROR) { quickcam->grabbing = 0; // stop the hardware and reinitialise the USB. if (quickcam_stop_isoc(quickcam)<0) return -1; if (quickcam_init_isoc(quickcam)<0) return -1; quickcam->scanstate = STATE_OK; // otherwise error forever! } #endif /* if we are not grabbing start grabbing */ if (!quickcam->grabbing) { if (debug&DEBUGLOGIC) printk("usb_quickcam_upload_frame start grabbing\n"); if (quickcam->sensor_ctrl.start(quickcam->dev, &(quickcam->sensor_ctrl))<0) { if (debug&DEBUGLOGIC) printk("usb_quickcam_upload_frame ...grabbing NOT started\n"); return -1; } if (usb_quickcam_set1(quickcam->dev, STV_ISO_ENABLE, 1)<0) { if (debug&DEBUGLOGIC) printk("usb_quickcam_upload_frame ...grabbing NOT started (iso enabling failed :()\n"); return -1; } quickcam->grabbing = -1; } return 0; } /* * Streamformat, as delivered by the camera: * * Raw image data for Bayer-RGB-matrix: * G R for even rows * B G for odd rows * Well not when frame by frame. It starts with 80 01 00 00 for the first time, * that 02 00 HH LL (and there is some grabages after the frame, * from time to time a 80 02 00 00 80 01 00 00 is received, it seems it is a * start of frame (at least it is handled as if it were one. * * Convert the Bayer-RGB to RBG24. * * Note: * * A trick is used to calculate medium value: * a long long cannot be used in 32 bits processors (like 386 family) * so I calculate a medium value per line, and sum the line values. * */ void quickcam_parse_data(struct usb_quickcam *quickcam, int curframe) { #define addblue 0 #define addgreen 1 #define addred 2 /* need cleaning ... */ #define XH quickcam->vwin.height #define XW quickcam->vwin.width #define SW quickcam->sensor_ctrl.width struct quickcam_frame *frame; long i; unsigned long mid_value=0; unsigned long mid_value_line=0; int xx=0; int yy=0; int xd=0; int copylen=0; unsigned char *o; unsigned char *data; int skiph; int startw, endw; /* calculate data to skip for the VV6410 sensor */ if (mode) { skiph = (quickcam->sensor_ctrl.height-(XH/2))/2; startw = (quickcam->sensor_ctrl.width-(XW/2))/2; } else { skiph = (quickcam->sensor_ctrl.height-XH)/2; startw = (quickcam->sensor_ctrl.width-XW)/2; } if (skiph<0) skiph=0; if (startw<0) startw=0; endw = quickcam->sensor_ctrl.width-startw; frame = &quickcam->frame[curframe]; if (debug&DEBUGDATA) printk("quickcam_parse_data %ld for frame %d\n", frame->storelength,curframe); /* Convert the current frame */ /* skip fisrt and last lines if needed */ data = frame->storedata+(skiph*quickcam->sensor_ctrl.width); for (i=skiph*quickcam->sensor_ctrl.width; istorelength-(skiph*quickcam->sensor_ctrl.width); i++) { /* * Skip line data when needed. */ if ((i%quickcam->sensor_ctrl.widthsensor_ctrl.width>=endw && startw)) { data++; continue; } /* * For HDCS Sensors: * Camera is G1R1 G2R2 G3R3... * B4G4 B5G5 B6G6... * Video is B4G1R1 B4G1R1 * B4G4R1 B4G4R1 * For Photobit... */ if (yy>XH) yy=XH; if (mode) { o=((char*)frame->data)+3*xd+(XW*3*yy); xd++; xd++; } else o=((char*)frame->data)+3*xx+(XW*3*yy); mid_value_line+=*data; if (interpolation == 1) { /* bilinear interpolation */ if (!(yy&1)) { // even row if (xx&1) { // odd column, red *(o+addred)=*data; if (yy == 0) { if (xx == XW-1) { *(o+addblue)=*(data+SW-1); *(o+addgreen)=(*(data-1) + *(data+SW))/2; } else { *(o+addblue)=(*(data+SW-1) + *(data+SW+1))/2; *(o+addgreen)=(*(data-1) + *(data+1) + *(data+SW))/3; } } else { if (xx == XW-1) { *(o+addblue)=(*(data-SW-1) + *(data+SW-1))/2; *(o+addgreen)=(*(data-SW) + *(data-1) + *(data+SW))/3; } else { *(o+addblue)=(*(data-SW-1) + *(data-SW+1) + *(data+SW-1) + *(data+SW+1))/4; *(o+addgreen)=(*(data-SW) + *(data-1) + *(data+1) + *(data+SW))/4; } } if (mode) { } } else { // green if (yy == 0) { if (xx == 0) { *(o+addred)=*(data+1); *(o+addblue)=*(data+SW); } else { *(o+addred)=(*(data-1) + *(data+1))/2; *(o+addblue)=*(data+SW); } } else { if (xx == 0) { *(o+addred)=*(data+1); *(o+addblue)=(*(data-SW) + *(data+SW))/2; } else { *(o+addred)=(*(data-1) + *(data+1))/2; *(o+addblue)=(*(data-SW) + *(data+SW))/2; } } *(o+addgreen)=*data; if (mode) { } } } else { if (xx&1) { // odd column green if (yy == XH-1) { if (xx == XW-1) { *(o+addred)=*(data-SW); *(o+addblue)=*(data-1); } else { *(o+addred)=*(data-SW); *(o+addblue)=(*(data-1) + *(data+1))/2; } } else { if (xx == XW-1) { *(o+addred)=(*(data-SW) + *(data+SW))/2; *(o+addblue)=*(data-1); } else { *(o+addred)=(*(data-SW) + *(data+SW))/2; *(o+addblue)=(*(data-1) + *(data+1))/2; } } *(o+addgreen)=*data; if (mode) { } } else { //blue *(o+addblue)=*data; if (yy == XH-1) { if (xx == 0) { *(o+addred)=*(data-SW+1); *(o+addgreen)=(*(data-SW) + *(data+1))/2; } else { *(o+addred)=(*(data-SW-1) + *(data-SW+1))/2; *(o+addgreen)=(*(data-SW) + *(data-1) + *(data+1))/3; } } else { if (xx == 0) { *(o+addred)=(*(data-SW+1) + *(data+SW+1))/2; *(o+addgreen)=(*(data-SW) + *(data+1) + *(data+SW))/3; } else { *(o+addred)=(*(data-SW-1) + *(data-SW+1) + *(data+SW-1) + *(data+SW+1))/4; *(o+addgreen)=(*(data-SW) + *(data-1) + *(data+1) + *(data+SW))/4; } } if (mode) { } } } } else { /* not bilinear interpolation */ if (!(yy&1)) { // even row if (xx&1) { // odd column, red *(o+addred)=*data; *(o+addred-3)=*data; *(o+addred+3*(XW))=*data; *(o+addred+3*(XW-1))=*data; if (mode) { *(o+addred+3)=*data; *(o+addred-6)=*data; *(o+addred+3*(XW+1))=*data; *(o+addred+3*(XW-2))=*data; } } else { // green *(o+1)=*data; *(o+1+3)=*data; if (mode) { *(o+1+6)=*data; if (xd>2) *(o+1-3)=*data; } } } else { if (xx&1) { // odd column green *(o+1)=*data; *(o+1-3)=*data; if (mode) { *(o+1+3)=*data; *(o+1-6)=*data; *(o+1+6)=*data; // last would be missing. } } else { //blue *(o+addblue)=*data; *(o+addblue+3)=*data; *(o+addblue+3*(XW))=*data; *(o+addblue+3*(XW-1))=*data; if (mode) { *(o+addblue+6)=*data; *(o+addblue+3*(XW+1))=*data; *(o+addblue-6)=*data; *(o+addblue+3*(XW-2))=*data; } } } } xx++; copylen ++; if (xx==XW || (xx==XW/2 && mode)) { if (mode) mid_value += (mid_value_line/(XW/2)); else mid_value += (mid_value_line/XW); mid_value_line = 0; xx=0; xd = 0; yy++; if (yy>=XH) { if (debug&DEBUGDATA) printk("skipped %ld bytes\n",frame->storelength-i); break; } } data++; } /* * the frame is done, the exposure should * be set here (We are the waked-up process) */ if (yy>=XH || (yy>=XH/2 && mode)) { if (quickcam->sensor_ctrl.mode==2) mid_value = mid_value/(yy/2); else mid_value = mid_value/yy; usb_quickcam_set_exposure(quickcam,mid_value); } /* if mode=2 we have only 1/2 of the enlargement */ if (quickcam->sensor_ctrl.mode==2) { data = frame->data; for (i=XH/2;i>1;i--) { memcpy(&data[i*6*XW],&data[i*3*XW],XW*3); memcpy(&data[i*6*XW-3*XW],&data[i*3*XW],XW*3); } // memcpy(&data[XW*6],&data,XW*3); copylen = copylen *2; } if (mode) copylen = copylen *2; frame->scanlength = (copylen*6)/2; // yes but how to compute partial pixels!. /* RGB -> BGR */ if(tobgr) { char c; char *p = frame->data; int i = quickcam->vwin.width * quickcam->vwin.height; while(--i) { c = p[0]; p[0] = p[2]; p[2] = c; p += 3; } } } /* * Analyse the data and store it * This routine has been written by Carlo E. Prelz, fluido@fluido.as * Image starts with 80 01 00 00 * and ends with 80 02 00 00 * The actual unknown messages are: * 80 05 00 00 (Assume it is start) * 80 06 00 00 (Assume it is end) * C0 01 00 00 (Assume it is start). * C0 02 00 00 (Assume it is end). * C0 06 00 00 (? like 80 06 00 00). * C0 05 00 00 (Not found in my tries, but I have the feeling it exists). * Chunk-Header 02 00 HH LL, followed by chunk with HHLL bytes * or 42 00 HH LL. */ static void quickcam_parse_store(struct urb *urb) { unsigned char type1,type2,*ptr; int i,cur_len,tot_len,left; unsigned int frag_len=0; struct quickcam_frame *pframe=NULL; int framesize; struct usb_quickcam *quickcam; if (!urb) return; quickcam = urb->context; if (!quickcam) return; if (!quickcam->dev) return; if (!quickcam->streaming) { if (debug&DEBUGINT) printk("quickcam: oops, not streaming, but interrupt\n"); return; } /* Use sensor information to get the framesize */ if (quickcam->sensor_ctrl.mode==1) framesize = qcmin(BAYER_FRAME_SIZE, (quickcam->sensor_ctrl.width* quickcam->sensor_ctrl.height)*2); else framesize = qcmin(BAYER_FRAME_SIZE, quickcam->sensor_ctrl.width* quickcam->sensor_ctrl.height); /* Check the length */ for(i=0,tot_len=quickcam->scratchlen;inumber_of_packets;i++) { if(!urb->iso_frame_desc[i].status) tot_len+=urb->iso_frame_desc[i].actual_length; else printk("quickcam data error: [%d] len=%d, status=%X\n", i, urb->iso_frame_desc[i].actual_length, urb->iso_frame_desc[i].status); } if(tot_len<=quickcam->scratchlen) return; if (tot_len>=SCRATCH_BUF_SIZE) { printk("quickcam_parse_store: scratch overflow\n"); return; } /* store the data (we skip the iso_frame in error) */ if (debug&DEBUGINT) printk("quickcam_parse_store: %d\n",tot_len); ptr=quickcam->scratch+quickcam->scratchlen; for(i=0;inumber_of_packets;i++) { if(!urb->iso_frame_desc[i].status && urb->iso_frame_desc[i].actual_length) { memcpy(ptr,urb->transfer_buffer+urb->iso_frame_desc[i].offset, urb->iso_frame_desc[i].actual_length); ptr+=urb->iso_frame_desc[i].actual_length; // usbvideo_HexDump(urb->transfer_buffer+urb->iso_frame_desc[i].offset,8); } } /* process the data */ pframe = NULL; if (quickcam->curframe!=-1) { if (quickcam->storestate==FRAME_STORE) { pframe = &quickcam->frame[quickcam->curframe]; } } if (debug&DEBUGINT) printk("quickcam_parse_store process data: %d curframe: %d\n",tot_len,quickcam->curframe); for(cur_len=0;cur_len+4<=tot_len;) { type1=quickcam->scratch[cur_len]; type2=quickcam->scratch[cur_len+1]; frag_len=quickcam->scratch[cur_len+2]<<8 | quickcam->scratch[cur_len+3]; if (debug&DEBUGINT) printk("quickcam_parse_store %d %02x %02x\n",frag_len,0xFF&type1, 0xFF&type2); if(type1==0x80 || type1==0xC0) { if(type2==1 || type2==5) { // a frame begins - see if we can capture it. if (pframe) { if (pframe->grabstate != FRAME_READY) { printk("quickcam: frame %d not free. Skipping...!\n", quickcam->curframe); pframe=NULL; } } else { // Check if we were waiting for a start of frame. if (quickcam->curframe!=-1) { pframe = &quickcam->frame[quickcam->curframe]; quickcam->storestate=FRAME_STORE; } } cur_len+=4; continue; } else if(type2==2 || type2==6) { // a frame end if(pframe) { if(pframe->storelength!=framesize && quickcam->sizechanged) { /* * That is possible that the size has been changed * during the frame capture in this case the next frames will have * the correct size. * The PB100 reacts a stange way it first changes the width: * oldwith * oldheight. * newwith * oldheight. * newwith * newheight. (Strange isn't it?). */ printk("quickcam: frame size is the old one? (%ld)\n", pframe->storelength); cur_len+=4; pframe->storelength = 0; pframe->grabstate = FRAME_READY; continue; } pframe->grabstate=FRAME_DONE; if(pframe->storelength!=framesize) printk("quickcam: frame size is incorrect! (%ld)\n", pframe->storelength); else quickcam->sizechanged = 0; if(waitqueue_active(&pframe->wq)) wake_up_interruptible(&pframe->wq); /* check if next one could be processed. */ if (quickcam->frame[(quickcam->curframe + 1) % 2].grabstate == FRAME_READY) { quickcam->curframe = (quickcam->curframe + 1) % 2; if (debug&DEBUGINT) printk("quickcam: marking as success next: %d\n", quickcam->curframe); pframe = &quickcam->frame[quickcam->curframe]; } else { pframe = NULL; if (debug&DEBUGINT) printk("quickcam: marking as success stop\n"); quickcam->curframe = -1; } } cur_len+=4; continue; } } else if(type1==0x02 || type1==0x42) { /* data */ if (4+frag_len+cur_len>tot_len) break; // not enough data. if(pframe) { if (debug&DEBUGINT) printk("quickcam: storing %d already in %ld\n",frag_len,pframe->storelength); if (pframe->grabstate==FRAME_READY) { pframe->grabstate = FRAME_GRABBING; pframe->storelength = 0; } if(pframe->storelength+frag_len>framesize) { if (pframe->storelength!=framesize) printk("quickcam: warning -> frame overflow (%ld)\n", pframe->storelength+frag_len-framesize); if (framesize>pframe->storelength) { // The framesize may be smaller as what is already store!. memcpy(pframe->storedata+pframe->storelength, quickcam->scratch+cur_len+4, framesize - pframe->storelength); } pframe->storelength=framesize; } else { memcpy(pframe->storedata+pframe->storelength, quickcam->scratch+cur_len+4,frag_len); pframe->storelength+=frag_len; } } else { quickcam->storestate=FRAME_SKIP; // skip until next frame. } cur_len+=4+frag_len; continue; } /* * must be garbage, and probably a big disaster... * Trying to find a start (VSYNC) as done in the old parse_data() could crash the box, * because we are in a interruption routine and we are blocking the box. * so we just dump the first 20 bytes (hopefully we find what to do with it one day, * as it had happened with the other sequences.). * */ if(pframe) printk("quickcam got garbage at %ld\n",pframe->storelength); else printk("quickcam got a garbage\n"); left = tot_len - cur_len; left = qcmin(20,left); usbvideo_HexDump(quickcam->scratch+cur_len,left); if(pframe) { pframe->grabstate=FRAME_ERROR; if(waitqueue_active(&pframe->wq)) wake_up_interruptible(&pframe->wq); } quickcam->scratchlen = 0; return; } /* save remeaning data, probably there nothing, as Carlo was just ignoring this case. */ left = tot_len - cur_len; if (left>0) { if (debug&DEBUGINT) printk("quickcam: save remeaning data: %d\n",left); memmove(quickcam->scratch, quickcam->scratch+cur_len, left); quickcam->scratchlen = left; } else quickcam->scratchlen = 0; } /* * Start isoc */ static int quickcam_init_isoc(struct usb_quickcam *quickcam) { struct urb *urb; int fx, err; if (debug&DEBUGLOGIC) printk("quickcam_init_isoc\n"); /* Alternate interface 3 is is the biggest frame size */ /* JFC use 1: but do not know why */ if (usb_set_interface(quickcam->dev, quickcam->iface, 1) < 0) { printk("usb_set_interface error\n"); return -EBUSY; } /* We double buffer the Iso lists */ urb = usb_alloc_urb(FRAMES_PER_DESC); if (!urb) { printk("quickcam_init_isoc: usb_init_isoc ret %d\n", 0); return -ENOMEM; } /* first buffer */ quickcam->sbuf[0].urb = urb; urb->dev = quickcam->dev; urb->context = quickcam; urb->pipe = usb_rcvisocpipe(quickcam->dev, _QUICKCAM_ISOPIPE); //printk("quickcam_init_isoc pipesize %d\n", usb_maxpacket (quickcam->dev, urb->pipe, usb_pipeout (urb->pipe))); urb->transfer_flags = USB_ISO_ASAP; urb->transfer_buffer = quickcam->sbuf[0].data; urb->complete = quickcam_parse_store; urb->number_of_packets = FRAMES_PER_DESC; urb->transfer_buffer_length = FRAME_SIZE_PER_DESC * FRAMES_PER_DESC; for (fx = 0; fx < FRAMES_PER_DESC; fx++) { urb->iso_frame_desc[fx].offset = FRAME_SIZE_PER_DESC * fx; urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC; } urb = usb_alloc_urb(FRAMES_PER_DESC); if (!urb) { printk("quickcam_init_isoc: usb_init_isoc ret %d\n", 0); return -ENOMEM; } /* second buffer */ quickcam->sbuf[1].urb = urb; urb->dev = quickcam->dev; urb->context = quickcam; urb->pipe = usb_rcvisocpipe(quickcam->dev, _QUICKCAM_ISOPIPE); urb->transfer_flags = USB_ISO_ASAP; urb->transfer_buffer = quickcam->sbuf[1].data; urb->complete = quickcam_parse_store; urb->number_of_packets = FRAMES_PER_DESC; urb->transfer_buffer_length = FRAME_SIZE_PER_DESC * FRAMES_PER_DESC; for (fx = 0; fx < FRAMES_PER_DESC; fx++) { urb->iso_frame_desc[fx].offset = FRAME_SIZE_PER_DESC * fx; urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC; } quickcam->sbuf[1].urb->next = quickcam->sbuf[0].urb; quickcam->sbuf[0].urb->next = quickcam->sbuf[1].urb; err = usb_submit_urb(quickcam->sbuf[0].urb); if (err) printk("quickcam_init_isoc: usb_submit_urb(0) ret %d\n", err); err = usb_submit_urb(quickcam->sbuf[1].urb); if (err) printk("quickcam_init_isoc: usb_submit_urb(1) ret %d\n", err); quickcam->streaming = 1; if (debug&DEBUGLOGIC) printk("quickcam_init_isoc finished\n"); return 0; } /* * Set size of window sensor, note that it is not yet center! * The mode is used to allow higher scan rate with smaller images. */ static int quickcam_set_size(struct usb_quickcam *quickcam, int width, int height) { if (height > 288 || height<0) return -1; if (width > 352 || width<0) return -1; if (quickcam->sensor_ctrl.set_size(quickcam->dev, mode)<0) { printk("set_size sensor failed\n"); return -1; } /* set the requested size (we should try to center it) */ if (quickcam->sensor_ctrl.set_window(quickcam->dev, 0, 0,width,height, &(quickcam->sensor_ctrl))<0) { printk("set_window sensor failed\n"); return(-1); } quickcam->vwin.width = width; quickcam->vwin.height = height; quickcam->sizechanged = -1; // changed! return(0); } /* * Initialise sensor and set shutter * The Photobit starts the pixel integration inmendiatly after * the reset... That why I moved this out usb_quickcam_configure(). */ int quickcam_init_sensor(struct usb_quickcam *quickcam); int quickcam_init_sensor(struct usb_quickcam *quickcam) { printf("-- called quickcam_init_sensor\n"); if (debug&DEBUGBASE) printk("quickcam_init_sensor: init sensor\n"); if (quickcam->sensor_ctrl.init(quickcam->dev,mode, &rgain, &bgain, &ggain, &(quickcam->sensor_ctrl))<0) { printk("Init sensor failed\n"); return(-1); } /* gain is the average of blue, green and red gain */ if(!keepexposure) { quickcam->gain =10; } quickcam->blue =qcmin(255,qcmax(2,bgain)); quickcam->red =qcmin(255,qcmax(2,rgain)); quickcam->green=qcmin(255,qcmax(2,ggain)); if (usb_quickcam_set_gains(quickcam)<0) { printk("set_gains sensor failed\n"); return(-1); } if (quickcam->sensor_ctrl.set_shutter(quickcam->dev,quickcam->shutter_val,0x100, &quickcam->sensor_ctrl)<0) { printk("set_shutter sensor failed\n"); return(-1); } /* Set the size otherwise the read() will fail */ /* * JFC have to arrange this .... and use the value from the quickcam structure! * NO: It is called in open() ... For the moment... */ if (quickcam_set_size(quickcam,352,288)<0) return(-1); return 0; } static int quickcam_stop_isoc(struct usb_quickcam *quickcam) { if (!quickcam->dev) return -1; if (debug&DEBUGLOGIC) printk("quickcam_stop_isoc\n"); /* Turn off continuous grab */ if (usb_quickcam_stop(quickcam) < 0) { printk("usb_quickcam_stop error\n"); return -EBUSY; } /* Set packet size to 0 */ if (usb_set_interface(quickcam->dev, quickcam->iface, 0) < 0) { printk("usb_set_interface error\n"); return -EINVAL; } quickcam->streaming = 0; /* make sure that the next is called */ if (quickcam->sbuf[1].urb) quickcam->sbuf[1].urb->next = NULL; if (quickcam->sbuf[0].urb) quickcam->sbuf[0].urb->next = NULL; /* Unschedule all of the iso td's */ if (quickcam->sbuf[1].urb) { usb_unlink_urb(quickcam->sbuf[1].urb); usb_free_urb(quickcam->sbuf[1].urb); quickcam->sbuf[1].urb = NULL; } if (quickcam->sbuf[0].urb) { usb_unlink_urb(quickcam->sbuf[0].urb); usb_free_urb(quickcam->sbuf[0].urb); quickcam->sbuf[0].urb = NULL; } return 0; } /* * Start grabing a new frame it must be the one required otherwise the calling * process will be lost (it waits for the wrong frame!). */ static int quickcam_new_frame(struct usb_quickcam *quickcam, int framenum) { struct quickcam_frame *frame; int width, height; if (debug&DEBUGLOGIC) printk("quickcam_new_frame %d\n",framenum); if (!quickcam->dev) return -1; if (quickcam->grabbing) { quickcam->frame[framenum].grabstate = FRAME_READY; quickcam->frame[framenum].scanlength = 0; if (quickcam->curframe == -1) quickcam->curframe = framenum; return 0; } /* If we're not grabbing a frame right now and the other frame is */ /* ready to be grabbed into, then use it instead */ if (quickcam->curframe == -1) { if (quickcam->frame[(framenum + 1) % 2].grabstate == FRAME_READY) framenum = (framenum + 1) % 2; } else { /* previous frame errored, make sure we restart using the other one */ if (quickcam->scanstate==STATE_ERROR) { //printk("grabing but STATE_ERROR (frame:%d)\n",quickcam->curframe); /* restart the grabing in the other frame */ framenum = (quickcam->curframe -1 + QUICKCAM_NUMFRAMES) % QUICKCAM_NUMFRAMES; } else { if (debug&DEBUGLOGIC) printk("quickcam_new_frame %d ending\n",quickcam->curframe); return 0; } } frame = &quickcam->frame[framenum]; width = frame->width; height = frame->height; /* Make sure it's not too big */ if (width > 352) width = 352; width = (width / 8) * 8; /* Multiple of 8 */ if (height > 288) height = 288; height = (height / 4) * 4; /* Multiple of 4 */ if (width<8) width=8; if (height<4) height=4; /* Set the ROI they want, xawtv tries a small piece first time. */ if (quickcam->vwin.height!=height && quickcam->vwin.width!=width) { if (quickcam_set_size(quickcam, width, height)<0) { if(debug&DEBUGLOGIC) printk("quickcam_new_frame roi failed... EBUSY\n"); return -EBUSY; } } /* set all the parameters for the irq modules */ frame->grabstate = FRAME_READY; frame->scanlength = 0; /* accumulated in quickcam_parse_data() */ quickcam->curframe = framenum; /* Grab the frame (will cause the irq's) */ if (usb_quickcam_upload_frame(quickcam) < 0) { if(debug&DEBUGLOGIC) printk("quickcam_upload_frame error\n"); quickcam->scanstate = STATE_ERROR; frame->grabstate = FRAME_UNUSED; quickcam->curframe = -1; quickcam->grabbing = 0; // not grabbing. return -EBUSY; } if (debug&DEBUGLOGIC) printk("quickcam_new_frame %d end success\n",framenum); return 0; } static int quickcam_init_done(struct video_device *dev) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) #if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS) create_proc_quickcam((struct usb_quickcam *) dev); #endif #endif return 0; } static int quickcam_get_depth(struct usb_quickcam *quickcam) { switch(quickcam->vpic.palette) { case VIDEO_PALETTE_RGB32: return 32; case VIDEO_PALETTE_RGB24: return 24; case VIDEO_PALETTE_YUV420: return 24; case VIDEO_PALETTE_YUV422: return 16; case VIDEO_PALETTE_YUYV: return 16; case VIDEO_PALETTE_RGB565: return 16; case VIDEO_PALETTE_RGB555: return 15; default: return -EINVAL; } } /* Video 4 Linux API */ static int quickcam_open(struct video_device *dev, int flags) { int err = -EBUSY; struct usb_quickcam *quickcam = (struct usb_quickcam *)dev; if (debug&DEBUGLOGIC) printk("quickcam_open\n"); down(&quickcam->lock); if (quickcam->user) goto out_unlock; // Re-Setup internal video_picture quickcam->vpic.hue = (rgain - bgain + 0xFF) << 7; quickcam->vpic.colour = ggain; quickcam->vpic.contrast = 32768; quickcam->vpic.brightness = bright; quickcam->vpic.whiteness = 0; quickcam->vpic.palette = VIDEO_PALETTE_RGB24; quickcam->vpic.depth = quickcam_get_depth(quickcam); // Re-Setup internal video_window quickcam->vwin.x = 0; quickcam->vwin.y = 0; quickcam->vwin.chromakey = 0; quickcam->vwin.flags = 30; /* 30 fps */ quickcam->frame[0].grabstate = FRAME_UNUSED; quickcam->frame[1].grabstate = FRAME_UNUSED; quickcam->streaming = 0; quickcam->curframe = -1; quickcam->scratchlen = 0; quickcam->storestate=FRAME_STORE; err = -ENOMEM; /* Allocate memory for the frame buffers */ quickcam->fbuf = rvmalloc(2 * MAX_FRAME_SIZE); if (!quickcam->fbuf) goto out_unlock; quickcam->frame[0].data = quickcam->fbuf; quickcam->frame[1].data = quickcam->fbuf + MAX_FRAME_SIZE; quickcam->sbuf[0].data = kmalloc (FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL); if (!quickcam->sbuf[0].data) goto open_err_on0; quickcam->sbuf[1].data = kmalloc (FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL); if (!quickcam->sbuf[1].data) goto open_err_on1; quickcam->frame[0].storedata = vmalloc (BAYER_FRAME_SIZE); if (!quickcam->frame[0].storedata) goto open_err_on2; quickcam->frame[1].storedata = vmalloc (BAYER_FRAME_SIZE); if (!quickcam->frame[1].storedata) goto open_err_on3; /* * Set default sizes in case IOCTL (VIDIOCMCAPTURE) is not used * (when using read()). */ quickcam->frame[0].width = 352; quickcam->frame[0].height = 288; quickcam->frame[0].bytes_read = 0; quickcam->frame[1].width = 352; quickcam->frame[1].height = 288; quickcam->frame[1].bytes_read = 0; /* initialise the USB things */ err = quickcam_init_isoc(quickcam); if (err) goto open_err_init; /* Start the sensor */ err = quickcam_init_sensor(quickcam); if (err) goto open_err_init_sensor; quickcam->user++; quickcam->grabbing=0; // we are not grabbing. quickcam->curframe=-1; // no frame in progress. quickcam->readframe = -1; // no frame beeing read up(&quickcam->lock); MOD_INC_USE_COUNT; if (debug&DEBUGLOGIC) printk("quickcam_open successfull\n"); return 0; open_err_init_sensor: quickcam_stop_isoc(quickcam); open_err_init: vfree (quickcam->frame[1].storedata); open_err_on3: vfree (quickcam->frame[0].storedata); open_err_on2: kfree (quickcam->sbuf[1].data); open_err_on1: kfree (quickcam->sbuf[0].data); open_err_on0: rvfree(quickcam->fbuf, 2 * MAX_FRAME_SIZE); out_unlock: up(&quickcam->lock); if (debug&DEBUGLOGIC) printk("quickcam_open failed\n"); return err; } static void quickcam_close(struct video_device *dev) { struct usb_quickcam *quickcam = (struct usb_quickcam *)dev; if (debug&DEBUGLOGIC) printk("quickcam_close\n"); down(&quickcam->lock); quickcam->user--; MOD_DEC_USE_COUNT; quickcam_stop_isoc(quickcam); quickcam->grabbing = 0; // not grabbing. quickcam->readframe = -1; // no frame beeing read quickcam->sizechanged = 0; // frame unchanged! rvfree(quickcam->fbuf, 2 * MAX_FRAME_SIZE); kfree(quickcam->sbuf[1].data); kfree(quickcam->sbuf[0].data); vfree(quickcam->frame[1].storedata); vfree(quickcam->frame[0].storedata); up(&quickcam->lock); if (!quickcam->dev) { video_unregister_device(&quickcam->vdev); kfree(quickcam); } } static long quickcam_write(struct video_device *dev, const char *buf, unsigned long count, int noblock) { return -EINVAL; } static int quickcam_ioctl(struct video_device *dev, unsigned int cmd, void *arg) { struct usb_quickcam *quickcam = (struct usb_quickcam *)dev; int retval = 0; if (!quickcam->dev) return -EIO; if (down_interruptible(&quickcam->busy_lock)) return -EINTR; switch (cmd) { case VIDIOCGCAP: { struct video_capability b; if (debug&DEBUGIOCTL) printk("quickcam_ioctl: VIDIOCGCAP\n"); strcpy(b.name, "Logitech USB Camera"); b.type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE; b.channels = 1; b.audios = 0; b.maxwidth = 352; /* CIF */ b.maxheight = 288; /* " */ b.minwidth = 176; b.minheight = 144; if (copy_to_user(arg, &b, sizeof(b))) retval = -EFAULT; break; } case VIDIOCGCHAN: { struct video_channel v; if (debug&DEBUGIOCTL) printk("quickcam_ioctl: VIDIOCGCHAN\n"); if (copy_from_user(&v, arg, sizeof(v))) { retval = -EFAULT; break; } if (v.channel != 0) { retval = -EINVAL; break; } v.flags = 0; v.tuners = 0; v.type = VIDEO_TYPE_CAMERA; strcpy(v.name, "Camera"); if (copy_to_user(arg, &v, sizeof(v))) retval = -EFAULT; break; } case VIDIOCSCHAN: { int v; if (copy_from_user(&v, arg, sizeof(v))) { retval = -EFAULT; break; } if (debug&DEBUGIOCTL) printk("quickcam_ioctl: VIDIOCSCHAN %d\n",v); if (v != 0) retval = -EINVAL; break; } case VIDIOCGPICT: { if (debug&DEBUGIOCTL) printk("quickcam_ioctl: VIDIOCGPICT\n"); quickcam->vpic.brightness = quickcam->brightness; quickcam->vpic.colour = quickcam->green<<8; quickcam->vpic.contrast = quickcam->shutter_val<<8; quickcam->vpic.hue = (quickcam->red - quickcam->blue + 0xff)<<7; if (copy_to_user(arg, &quickcam->vpic, sizeof(quickcam->vpic))) retval = -EFAULT; break; } case VIDIOCSPICT: { struct video_picture p; if (debug&DEBUGIOCTL) printk("quickcam_ioctl: VIDIOCSPICT\n"); if (copy_from_user(&p, arg, sizeof(p))) retval = -EFAULT; else { if(p.brightness != quickcam->vpic.brightness) { quickcam->vpic.brightness = p.brightness; quickcam->brightness = qcmin(0xfd00,qcmax(0x100,quickcam->vpic.brightness)); } if(p.contrast != quickcam->vpic.contrast) { quickcam->vpic.contrast = p.contrast; quickcam->shutter_val = qcmin(0xfd,qcmax(1,quickcam->vpic.contrast>>8)); quickcam->sensor_ctrl.set_shutter(quickcam->dev,quickcam->shutter_val,0x100); } if(p.colour != quickcam->vpic.colour) { quickcam->vpic.colour = p.colour; quickcam->green = qcmin(0xfd,qcmax(1,quickcam->vpic.colour>>8)); usb_quickcam_set_gains(quickcam); } if(p.hue != quickcam->vpic.hue) { quickcam->vpic.hue = p.hue; quickcam->red = qcmin(0xfd,qcmax(1,quickcam->vpic.hue>>8)); quickcam->blue = 0xff-quickcam->red; usb_quickcam_set_gains(quickcam); } /* xawtv use this to find the supported format */ if (debug&DEBUGIOCTL) printk("quickcam_ioctl: VIDIOCSPICT depth: %d palette %d\n",p.depth, p.palette); retval = IsSupported(p.palette); if (!retval) quickcam->vpic.palette = p.palette; } if (debug&DEBUGIOCTL) printk("quickcam_ioctl: VIDIOCSPICT return %d\n",retval); break; } case VIDIOCSWIN: { struct video_window vw; if (debug&DEBUGIOCTL) printk("quickcam_ioctl: VIDIOCSWIN vw.flags %d vw.clipcount %d\n",vw.flags,vw.clipcount); if (copy_from_user(&vw, arg, sizeof(vw))) { retval = -EFAULT; break; } /* set the size only if changed */ if (quickcam->vwin.width != vw.width || quickcam->vwin.height != vw.height) { // Set requested size. if (quickcam_set_size(quickcam,vw.width,vw.height)<0) retval = -EINVAL; } if (debug&DEBUGIOCTL) printk("quickcam_ioctl: VIDIOCSWIN returns %d\n",retval); break; } case VIDIOCGWIN: { if (debug&DEBUGIOCTL) printk("quickcam_ioctl: VIDIOCGWIN\n"); if (copy_to_user(arg, &quickcam->vwin, sizeof(quickcam->vwin))) retval = -EFAULT; break; } case VIDIOCGMBUF: { struct video_mbuf vm; if (debug&DEBUGIOCTL) printk("quickcam_ioctl: VIDIOCGMBUF\n"); memset(&vm, 0, sizeof(vm)); vm.size = MAX_FRAME_SIZE * 2; vm.frames = 2; vm.offsets[0] = 0; vm.offsets[1] = MAX_FRAME_SIZE; if (copy_to_user((void *)arg, (void *)&vm, sizeof(vm))) retval = -EFAULT; break; } case VIDIOCMCAPTURE: { struct video_mmap vm; if (debug&DEBUGIOCTL) printk("quickcam_ioctl: VIDIOCMCAPTURE\n"); if (copy_from_user((void *)&vm, (void *)arg, sizeof(vm))) { retval = -EFAULT; break; } if (debug&DEBUGIOCTL) printk("quickcam_ioctl: frame: %d, size: %dx%d, format: %d\n", vm.frame, vm.width, vm.height, vm.format); /* check supported formats */ retval = IsSupported(vm.format); if (retval) break; /* format not supported */ quickcam->vpic.palette = vm.format; if ((vm.frame != 0) && (vm.frame != 1)) { retval = -EINVAL; break; } if (quickcam->frame[vm.frame].grabstate == FRAME_GRABBING) { //printk("frame: %d, FRAME_GRABBING\n", vm.frame); retval = -EBUSY; break; } quickcam->frame[vm.frame].width = vm.width; quickcam->frame[vm.frame].height = vm.height; /* Mark it as ready */ quickcam->frame[vm.frame].grabstate = FRAME_READY; retval = quickcam_new_frame(quickcam, vm.frame); break; } case VIDIOCSYNC: { int frame,retry; if (copy_from_user((void *)&frame, arg, sizeof(int))) { retval = -EFAULT; break; } if (debug&DEBUGIOCTL) printk("quickcam_ioctl: VIDIOCSYNC for %d (%d)\n", frame,quickcam->frame[frame].grabstate); if (debug&DEBUGIOCTL) printk("quickcam_ioctl: VIDIOCSYNC to frame %d\n", frame); for (retry=0;retry<3;retry++) { if (!quickcam->dev) { retval = -EIO; break; } /* Check that CAPTURE was done for this frame */ if (quickcam->frame[frame].grabstate==FRAME_UNUSED) { retval = -EINVAL; break; } /* if error we retry */ else if (quickcam->frame[frame].grabstate==FRAME_ERROR) { if (debug&DEBUGIOCTL) printk("quickcam_ioctl: error retrying!\n"); /* if (usb_quickcam_set1(quickcam->dev, STV_ISO_ENABLE, 0)<0) { printk("usb_quickcam_set1 failed!\n"); retval = -EBUSY; break; } */ quickcam->scratchlen = 0; // discard buffer. if ((retval = usb_quickcam_upload_frame(quickcam)) < 0) break; quickcam->frame[frame].grabstate = FRAME_GRABBING; quickcam->frame[frame].scanlength = 0; } else if (quickcam->frame[frame].grabstate==FRAME_DONE) { break; } else { do { interruptible_sleep_on(&quickcam->frame[frame].wq); if (signal_pending(current)) { if (debug&DEBUGIOCTL) printk("quickcam_ioctl: VIDIOCSYNC alarmed (%d:%d)\n",quickcam->frame[frame].grabstate,quickcam->scanstate); /* try JFC */ quickcam->frame[frame].grabstate = FRAME_ERROR; quickcam->scanstate = STATE_ERROR; quickcam->scratchlen = 0; quickcam->grabbing = 0; // I am looking another to detect it. retval = -EINTR; break; } } while (quickcam->frame[frame].grabstate == FRAME_GRABBING || quickcam->frame[frame].grabstate == FRAME_READY); if (retval) break; } } /* too many errors, return error */ if (retry==3 && quickcam->frame[frame].grabstate == FRAME_ERROR) { retval = -EBUSY; break; } /* * stop isostream if we have all what we need. */ if (quickcam->curframe==-1) { usb_quickcam_stop(quickcam); quickcam->grabbing = 0; } /* Convert the Bayer to RBG24 and set gains & exposure */ quickcam_parse_data(quickcam,frame); if (quickcam->vpic.palette!=VIDEO_PALETTE_RGB24) quickcam_convert_image(&quickcam->frame[frame],quickcam->vpic.palette); if (debug&DEBUGIOCTL) printk("quickcam_ioctl: VIDIOCSYNC got %d (%d) RC:%d)\n", frame,quickcam->frame[frame].grabstate,retval); quickcam->frame[frame].grabstate = FRAME_UNUSED; break; } case VIDIOCGFBUF: { struct video_buffer vb; if (debug&DEBUGIOCTL) printk("quickcam_ioctl: VIDIOCGFBUF\n"); memset(&vb, 0, sizeof(vb)); vb.base = NULL; /* frame buffer not supported, not used */ if (copy_to_user((void *)arg, (void *)&vb, sizeof(vb))) retval = -EFAULT; break; } case VIDIOCKEY: break; case VIDIOCCAPTURE: retval = -EINVAL; break; case VIDIOCSFBUF: retval = -EINVAL; break; case VIDIOCGTUNER: case VIDIOCSTUNER: retval = -EINVAL; break; case VIDIOCGFREQ: case VIDIOCSFREQ: retval = -EINVAL; break; case VIDIOCGAUDIO: case VIDIOCSAUDIO: retval = -EINVAL; break; default: retval = -ENOIOCTLCMD; break; } up(&quickcam->busy_lock); return retval; } static long quickcam_read(struct video_device *dev, char *buf, unsigned long count, int noblock) { struct usb_quickcam *quickcam = (struct usb_quickcam *)dev; int frmx = -1; struct quickcam_frame *frame; int retry=0; if (debug&DEBUGREAD) printk("quickcam_read: %ld bytes, noblock=%d\n", count, noblock); if (!dev || !buf) { if (debug&DEBUGREAD) printk("quickcam_read: no video_device available or no buffer attached :( EFAULT\n"); return -EFAULT; } if (!quickcam->dev) { if (debug&DEBUGREAD) printk("quickcam_read: no quickcam_device available :( EIO\n"); return -EIO; } if (down_interruptible(&quickcam->busy_lock)) { if (debug&DEBUGREAD) printk("quickcam_read: quickcam_device busy :( EINTR\n"); return -EINTR; } /* check if we have already started to read */ if (quickcam->readframe!=-1) { frmx = quickcam->readframe; // well I could have put a goto here... frame = &quickcam->frame[frmx]; if (debug&DEBUGREAD) printk("quickcam_read: we didn't start reading yet...start now\n"); goto read; // damned I have done it... } /* See if a frame is completed, then use it. */ if (frmx == - 1) { if (debug&DEBUGREAD) printk("quickcam_read: check if a frame is already completed... "); if (quickcam->frame[0].grabstate >= FRAME_DONE) /* _DONE or _ERROR */ frmx = 0; else if (quickcam->frame[1].grabstate >= FRAME_DONE)/* _DONE or _ERROR */ frmx = 1; if (debug&DEBUGREAD) { /* Not mine jfclere */ if (frmx == -1) printk("NO\n"); else printk("YES\n"); } } if (noblock && (frmx == -1)) { if (debug&DEBUGREAD) printk("quickcam_read: blocked device :( EAGAIN\n"); up(&quickcam->busy_lock); return -EAGAIN; } /* If no FRAME_DONE, look for a FRAME_GRABBING state. */ /* See if a frame is in process (grabbing), then use it. */ if (frmx == -1) { if (debug&DEBUGREAD) printk("quickcam_read: checking wheter something is grabbing at the moment... "); if (quickcam->frame[0].grabstate == FRAME_GRABBING) frmx = 0; else if (quickcam->frame[1].grabstate == FRAME_GRABBING) frmx = 1; if (debug&DEBUGREAD) { /* Not mine jfclere */ if (frmx == -1) printk("NO\n"); else printk("YES\n"); } } /* If no frame is active, start one. */ if (frmx == -1) { if (debug&DEBUGREAD) printk("quickcam_read: no active frames....start new one\n"); /* grabbing or not grabbing */ if (!quickcam->grabbing || quickcam->curframe==-1) { if (debug&DEBUGREAD) printk("quickcam_read: we are not grabbing....start it\n"); quickcam_new_frame(quickcam, 0); quickcam_new_frame(quickcam, 1); /* allow double buffering */ } if (debug&DEBUGREAD) printk("quickcam_read: waiting for the frame to finish...\n"); frmx = quickcam->curframe; /* we have to wait for the one in progress */ } if (frmx == -1) frmx = 0; frame = &quickcam->frame[frmx]; if (debug&DEBUGREAD) printk("quickcam_read: using %d state %d\n", frmx, frame->grabstate); restart: if (debug&DEBUGREAD) printk("quickcam_read: goto restart called\n"); if (!quickcam->dev) { if (debug&DEBUGREAD) printk("quickcam_read: no quickcam_device available :( EIO\n"); up(&quickcam->busy_lock); return -EIO; } while (frame->grabstate == FRAME_READY || frame->grabstate == FRAME_GRABBING) { //if (debug&DEBUGREAD) //printk("quickcam_read: in FRAME_READY || FRAME_GRABBING loop ...waiting\n"); interruptible_sleep_on(&frame->wq); if (signal_pending(current)) { if (debug&DEBUGREAD) printk("quickcam_read: aborting... :( EINTR\n"); up(&quickcam->busy_lock); return -EINTR; } } if (frame->grabstate != FRAME_DONE) { frame->bytes_read = 0; if (debug&DEBUGREAD) printk("quickcam_read: errored frame %d\n", quickcam->curframe); if (quickcam_new_frame(quickcam, frmx)) { if (debug&DEBUGREAD) printk("quickcam_read: quickcam_new_frame error\n"); } retry++; if (retry<=3) goto restart; up(&quickcam->busy_lock); return -EINTR; } /* Convert the Bayer to RBG24 and set gains & exposure */ quickcam_parse_data(quickcam,frmx); if (quickcam->vpic.palette!=VIDEO_PALETTE_RGB24) quickcam_convert_image(&quickcam->frame[frmx],quickcam->vpic.palette); read: if (debug&DEBUGREAD) printk("quickcam_read: frmx=%d, bytes_read=%ld, scanlength=%ld\n", frmx, frame->bytes_read, frame->scanlength); /* copy bytes to user space; we allow for partials reads */ quickcam->readframe = frmx; if ((count + frame->bytes_read) > frame->scanlength) count = frame->scanlength - frame->bytes_read; if(copy_to_user(buf, frame->data + frame->bytes_read, count)) { up(&quickcam->busy_lock); return -EFAULT; } frame->bytes_read += count; if (debug&DEBUGREAD) printk("quickcam_read: {copy} count used=%ld, new bytes_read=%ld\n", count, frame->bytes_read); if (frame->bytes_read >= frame->scanlength) { /* All data has been read */ frame->bytes_read = 0; quickcam->readframe = -1; // we have finished. /* Mark it as available to be used again. */ quickcam->frame[frmx].grabstate = FRAME_UNUSED; if (quickcam_new_frame(quickcam, frmx)) printk(KERN_ERR "quickcam_read: quickcam_new_frame returned error\n"); } up(&quickcam->busy_lock); return count; } static int quickcam_mmap(struct video_device *dev, const char *adr, unsigned long size) { struct usb_quickcam *quickcam = (struct usb_quickcam *)dev; unsigned long start = (unsigned long)adr; unsigned long page, pos; if (!quickcam->dev) return -EIO; if (size > (((2 * MAX_FRAME_SIZE) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))) return -EINVAL; if (down_interruptible(&quickcam->busy_lock)) return -EINTR; pos = (unsigned long)quickcam->fbuf; while (size > 0) { page = kvirt_to_pa(pos); if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED)) { up(&quickcam->busy_lock); return -EAGAIN; } start += PAGE_SIZE; pos += PAGE_SIZE; if (size > PAGE_SIZE) size -= PAGE_SIZE; else size = 0; } up(&quickcam->busy_lock); return 0; } static struct video_device quickcam_template = { name: "Logitech Quickcam Express USB", type: VID_TYPE_CAPTURE, hardware: VID_HARDWARE_QCE, initialize: quickcam_init_done, open: quickcam_open, close: quickcam_close, read: quickcam_read, write: quickcam_write, ioctl: quickcam_ioctl, mmap: quickcam_mmap, }; int usb_quickcam_configure(struct usb_quickcam *quickcam) { struct usb_device *dev = quickcam->dev; unsigned char id[2]; struct sensor_data *sensor; /* Set altsetting 0 */ if (usb_set_interface(dev, quickcam->iface, 0) < 0) { printk("usb_set_interface error\n"); return -EBUSY; } if (usb_quickcam_set1(dev, STV_ISO_ENABLE, 0) < 0) { printk("usb_quickcam_set1 STV_ISO_ENABLE(0) failed\n"); return -EBUSY; } /* Probe for the sensor type. */ for (sensor = sensors; sensor->name; sensor++) { if (usb_quickcam_set1(dev, STV_REG23, sensor->reg23) < 0) { printk("usb_quickcam_set1 STV_REG23 failed\n"); return -EBUSY; } if (usb_quickcam_get_i2c(dev, sensor->i2c_addr, sensor->id_reg, id, sensor->length_id) < 0) { printk("usb_quickcam_get_i2c error\n"); return -EBUSY; } printk("quickcam: probe of %s sensor = %02x %02x id: %02x\n", sensor->name, id[0], id[1],sensor->id); if (id[sensor->length_id-1] == sensor->id) break; } if (!sensor->name) { printk("quickcam: unsupported sensor\n"); return -EBUSY; } printk("quickcam: %s sensor detected\n", sensor->name); quickcam->sensor_name = sensor->name; quickcam->sensor_addr = sensor->i2c_addr; sensor->load(&quickcam->sensor_ctrl); memcpy(&quickcam->vdev, &quickcam_template, sizeof(quickcam_template)); init_waitqueue_head(&quickcam->frame[0].wq); init_waitqueue_head(&quickcam->frame[1].wq); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,5) if (video_register_device(&quickcam->vdev, VFL_TYPE_GRABBER, video_nr) == -1) #else if (video_register_device(&quickcam->vdev, VFL_TYPE_GRABBER) == -1) #endif { printk("video_register_device failed\n"); return -EBUSY; } // Disable data stream. if (usb_quickcam_set1(dev, STV_ISO_ENABLE, 0) < 0) { printk("usb_quickcam_set1 STV_ISO_ENABLE(1) failed\n"); goto error; } if (usb_quickcam_set1(dev, STV_REG23, 1) < 0) { /* I see no reason that the others failed if this one is OK */ printk("usb_quickcam_set1 STV_REG23(1) failed\n"); goto error; } /* set default values for the camera */ if(bright<=0 || bright>=65535) bright=EXPO_VAL; quickcam->brightness = bright; quickcam->shutter_val= SHUTTER_VAL; quickcam->scanstate = STATE_OK; quickcam->x = quickcam->yy=0; quickcam->readframe = -1; // Setup internal video_picture quickcam->vpic.hue = (rgain -bgain +0xFF) << 7; quickcam->vpic.colour = ggain; quickcam->vpic.contrast = 32768; quickcam->vpic.brightness = bright; quickcam->vpic.whiteness = 0; quickcam->vpic.palette = VIDEO_PALETTE_RGB24; quickcam->vpic.depth = quickcam_get_depth(quickcam); // Setup internal video_window quickcam->vwin.x = 0; quickcam->vwin.y = 0; quickcam->vwin.chromakey = 0; quickcam->vwin.flags = 30; /* 30 fps */ return 0; error: video_unregister_device(&quickcam->vdev); usb_driver_release_interface(&quickcam_driver, &dev->actconfig->interface[0]); return -EBUSY; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) static void *quickcam_probe(struct usb_device *dev, unsigned int ifnum, const struct usb_device_id *id) #else static void *quickcam_probe(struct usb_device *dev, unsigned int ifnum) #endif { struct usb_interface_descriptor *interface; struct usb_quickcam *quickcam; /* We don't handle multi-config cameras */ if (dev->descriptor.bNumConfigurations != 1) return NULL; interface = &dev->actconfig->interface[ifnum].altsetting[0]; /* Is it a Quickcam? */ printk("USB Quickcam Class %x SubClass %x idVendor %x idProduct %x\n", interface->bInterfaceClass, interface->bInterfaceSubClass, dev->descriptor.idVendor, dev->descriptor.idProduct); #if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,255) if (dev->descriptor.idVendor != 0x046d) return NULL; if (dev->descriptor.idProduct != 0x0840 && dev->descriptor.idProduct != 0x0850 && dev->descriptor.idProduct != 0x0870) return NULL; #endif /* * Checking vendor/product is not enough * In case on Quickcam Web the audio is at class 1 and subclass 1/2. * ono /dev/dsp and one /dev/mixer */ if (interface->bInterfaceClass != 0xFF) return NULL; if (interface->bInterfaceSubClass != 0xFF) return NULL; /* We found a Quickcam */ printk("USB Quickcam camera found using: %s\n", VERSION); if ((quickcam = kmalloc(sizeof(*quickcam), GFP_KERNEL)) == NULL) { printk("couldn't kmalloc quickcam struct\n"); return NULL; } memset(quickcam, 0, sizeof(*quickcam)); quickcam->dev = dev; quickcam->iface = interface->bInterfaceNumber; if (!usb_quickcam_configure(quickcam)) { quickcam->user=0; init_MUTEX(&quickcam->lock); /* to 1 == available */ init_MUTEX(&quickcam->busy_lock); /* to 1 == available */ return quickcam; } else { kfree(quickcam); return NULL; } } static void quickcam_disconnect(struct usb_device *dev, void *ptr) { struct usb_quickcam *quickcam = (struct usb_quickcam *) ptr; if (!quickcam) return; /* We don't want people trying to open up the device */ if (!quickcam->user) video_unregister_device(&quickcam->vdev); usb_driver_release_interface(&quickcam_driver, &quickcam->dev->actconfig->interface[0]); quickcam->dev = NULL; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) #if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS) destroy_proc_quickcam(quickcam); #endif #endif quickcam->frame[0].grabstate = FRAME_ERROR; quickcam->frame[1].grabstate = FRAME_ERROR; quickcam->curframe = -1; /* This will cause the process to request another frame. */ if (waitqueue_active(&quickcam->frame[0].wq)) wake_up_interruptible(&quickcam->frame[0].wq); if (waitqueue_active(&quickcam->frame[1].wq)) wake_up_interruptible(&quickcam->frame[1].wq); quickcam->streaming = 0; /* Unschedule all of the iso td's */ if (quickcam->sbuf[1].urb) { quickcam->sbuf[1].urb->next = NULL; usb_unlink_urb(quickcam->sbuf[1].urb); usb_free_urb(quickcam->sbuf[1].urb); quickcam->sbuf[1].urb = NULL; } if (quickcam->sbuf[0].urb) { quickcam->sbuf[0].urb->next = NULL; usb_unlink_urb(quickcam->sbuf[0].urb); usb_free_urb(quickcam->sbuf[0].urb); quickcam->sbuf[0].urb = NULL; } /* Free the memory */ if (!quickcam->user) kfree(quickcam); } static struct usb_driver quickcam_driver = { name: "quickcam", #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) id_table: device_table, #endif probe: quickcam_probe, disconnect: quickcam_disconnect, }; static int __init usb_quickcam_init(void) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) #if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS) proc_quickcam_create(); #endif #endif return usb_register(&quickcam_driver); } static void __exit usb_quickcam_cleanup(void) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) #if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS) proc_quickcam_destroy(); #endif #endif usb_deregister(&quickcam_driver); } module_init(usb_quickcam_init); module_exit(usb_quickcam_cleanup); #endif /* !__FreeBSD__ , end of file */ pwcbsd/qce-ga-0.40d/quickcam.h000644 000423 000000 00000014435 10551677522 016445 0ustar00luigiwheel000000 000000 #ifndef __LINUX_QUICKCAM_H #define __LINUX_QUICKCAM_H #if defined(__FreeBSD__) #include "pwc.h" #define up(x) do {} while (0) #define down_interruptible(x) 0 #define copy_to_user(a, b, c) 0 /* ioctl copy from/to user */ #define copy_from_user(a, b, c) 0 #define interruptible_sleep_on(x) do {} while (0) #define signal_pending(x) 0 #define KERN_ERR "qce-ga: " #else #include #include #include #endif #include "videodev.h" #define _QUICKCAM_ISOPIPE 0x81 #define SHUTTER_VAL 0x80 #define EXPO_VAL 0xa0 #define RGAIN_DEF 0x80 #define BGAIN_DEF 0x80 #define GGAIN_DEF 0x80 // define debug levels #define DEBUGBASE 1 #define DEBUGLOGIC 2 #define DEBUGDATA 4 #define DEBUGREAD 8 #define DEBUGIOCTL 16 #define DEBUGINT 32 #define VID_HARDWARE_QCE 50 // Control register of the STV0600 ASIC #define STV_ISO_ENABLE 0x1440 #define STV_SCAN_RATE 0x1443 #define STV_ISO_SIZE 0x15c1 #define STV_Y_CTRL 0x15c3 #define STV_X_CTRL 0x1680 #define STV_REG00 0x1500 #define STV_REG01 0x1501 #define STV_REG02 0x1502 #define STV_REG03 0x1503 #define STV_REG04 0x1504 #define STV_REG23 0x0423 #define STREAM_BUF_SIZE (PAGE_SIZE * 4) // #define SCRATCH_BUF_SIZE (STREAM_BUF_SIZE * 2) // #define FRAMES_PER_DESC 10 // #define FRAME_SIZE_PER_DESC 1023 // Shouldn't be hardcoded JFC was 960 // 356*292 from VV6410 352*288 otherwise. // max size of frame received from the camera #define BAYER_FRAME_SIZE 103952 #define QUICKCAM_NUMFRAMES 2 #define QUICKCAM_NUMSBUF 2 /* scanstate */ enum { STATE_OK, // Ok. STATE_ERROR, // An error has been detected. }; /* store state, that to know what to do with the frame */ enum { FRAME_STORE, // Ok. FRAME_SKIP, // we have skipped data the frame is partial. }; #if 0 /* spca5xx.h */ /* grabstate (of the frame). */ enum { FRAME_UNUSED, /* Unused (no MCAPTURE) */ FRAME_READY, /* Ready to start grabbing */ FRAME_GRABBING, /* In the process of being grabbed into */ FRAME_DONE, /* Finished grabbing, but not been synced yet */ FRAME_ERROR, /* Something bad happened while processing */ }; #endif struct usb_device; struct quickcam_sbuf { char *data; struct urb *urb; }; struct qc_palette_list { int num; char *name; int supported; }; #if 0 /* * Structure filled in for each of the types of sensor (HDCS, PB0100) */ struct sensorctrl { int (*init) (struct usb_device * dev, int mode, int *rgain, int *bgain, int *ggain, struct sensorctrl *sensor_ctrl); int (*set_shutter) (struct usb_device * dev, int sval, int xval); int (*set_gains) (struct usb_device * dev, int rgain, int bgain, int ggain); int (*set_window) (struct usb_device * dev, int x, int y, int w, int h, struct sensorctrl *sensor_ctrl); int (*set_size) (struct usb_device * dev, int mode); int (*start) (struct usb_device * dev, struct sensorctrl *sensor_ctrl); int (*stop) (struct usb_device * dev, struct sensorctrl *sensor_ctrl); // size delivered by the sensor. int width; int height; // mode: 0 full; 1 half; 2 quater. int mode; }; #endif /** * Structure storing the I2C messages. * Do use it: * usb_quickcam_i2c_new reset the structure. * usb_quickcam_i2c_add add register and value. * usb_quickcam_i2c_send send it to the sensor. It call a usb_quickcam_i2c_new. */ struct quickcam_i2c { int length; unsigned char buff[35]; }; struct quickcam_frame { char *data; /* Frame buffer */ long scanlength; /* uncompressed, raw data length of frame */ char *storedata; /* Bayer data */ long storelength; /* len of received data */ int width; /* Width application is expecting */ int height; /* Height */ // int hdrwidth; /* Width the frame actually is */ // int hdrheight; /* Height */ volatile int grabstate; /* State of grabbing */ long bytes_read; /* amount of scanlength that has been read from *data */ #if !defined(__FreeBSD__) wait_queue_head_t wq; /* Processes waiting */ #endif }; struct usb_quickcam { #if !defined(__FreeBSD__) struct video_device vdev; /* This _must_ be in the beginning of the structure! */ struct semaphore lock; #endif struct video_picture vpic; struct video_window vwin; /* Device structure */ struct usb_device *dev; /* For /proc interface */ struct proc_dir_entry *proc_entry; unsigned char iface; int user; /* user count for exclusive use */ int streaming; /* Are we streaming Isochronous? */ int grabbing; /* Are we grabbing? */ int readframe; /* the frame we are reading. */ int scanstate; /* state of the automaton */ int storestate; /* indicated that we have to wait for the end */ int sizechanged; /* indicated size changes */ int yy; int x; int shutter_val; int gain; /* global gain */ unsigned long val; /* global exposure */ int blue; /* blue gain */ int red; /* red gain */ int green;/* green gain */ int brightness;/* Control brightness of image through V4L */ char *sensor_name; /* for /proc */ unsigned char sensor_addr; /* hdcs and photobit have different addr */ char *fbuf; /* Videodev buffer area */ int curframe; struct quickcam_frame frame[QUICKCAM_NUMFRAMES]; /* Double buffering */ #if !defined(__FreeBSD__) struct quickcam_sbuf sbuf[QUICKCAM_NUMSBUF]; /* Double buffering */ /* Scratch space from the Isochronous pipe */ unsigned char scratch[SCRATCH_BUF_SIZE]; int scratchlen; struct semaphore busy_lock; /* guard against SMP multithreading */ #endif struct sensorctrl sensor_ctrl; /* specific routines for a sensor */ }; /* Add prototyping to prevent warnings */ /* Sensor commmon routines */ void usb_quickcam_i2c_new(struct quickcam_i2c *i2cbuff); void usb_quickcam_i2c_add(struct quickcam_i2c *i2cbuff,unsigned char reg, unsigned char value); void usb_quickcam_i2c_add2(struct quickcam_i2c *i2cbuff,unsigned char reg, unsigned short value); int usb_quickcam_i2c_send(struct usb_device *dev, struct quickcam_i2c *i2cbuff, unsigned char sensor_add); /* USB interface chip control */ #if 0 int usb_quickcam_set1(struct usb_device *dev, short reg, char val); #endif int usb_quickcam_set2(struct usb_device *dev, short reg, short val); /* conversion routines */ int IsSupported(int format); void quickcam_convert_image(struct quickcam_frame *frame, int format); char *FormatName(int format); #endif pwcbsd/qce-ga-0.40d/quickcam.sh000755 000423 000000 00000001754 10546676144 016636 0ustar00luigiwheel000000 000000 # # Load the needed modules for the quickcam driver. # # you may need these commented out lines if your insmod is too old. #IS24=`uname -r | grep -c ^2.2` # #if [ $IS24 == "1" ] #then #insmod /lib/modules/2.2.14/misc/videodev.o #else #insmod /lib/modules/2.4.0-test10/kernel/drivers/media/video/videodev.o #fi # # Load the modules. # # Video4Linux Support /sbin/insmod videodev # USB Core Module /sbin/insmod usbcore # USB UHCI/OHCI Controller Modules (new) /sbin/insmod usb-uhci /sbin/insmod usb-ohci # USB UHCI Controller Modules (old) /sbin/insmod uhci # that for the sys request key, comment it out if the quickcam works well. # if you get any erors: use Alt + SysRq + S = Emergency Sync (write everything on HDD) # use Alt + SysRq + U = Unmount all HDD's # use Alt + SysRq + B = Reboot system immediatly echo "1" > /proc/sys/kernel/sysrq # quickcam.o is in current directory, after copying in /lib/modules/.../misc, # use insmod quickcam. /sbin/insmod mod_quickcam.o /sbin/lsmod pwcbsd/qce-ga-0.40d/vv6410.c000644 000423 000000 00000024321 10551677522 015604 0ustar00luigiwheel000000 000000 /* * qce-ga, linux V4L driver for the Quickcam Express and Dexxa Quickcam * * vv6410.c - VV6410 Sensor Implementation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "quickcam.h" #include "helper.h" #include "vv6410.h" /** * Set window size */ #if defined(__FreeBSD__) #define isaweb(DEV) (UGETW((DEV)->ddesc.idProduct)==0x850) #else #define isaweb(DEV) ((DEV)->descriptor.idProduct==0x850) #endif static int vv6410_set_window(struct usb_device *dev, int x, int y, int width, int height, struct sensorctrl *sensor_ctrl) { struct quickcam_i2c i2cbuff; usb_quickcam_i2c_new(&i2cbuff); // x offset x = qcmax(1,x); usb_quickcam_i2c_add(&i2cbuff,0x57,x >> 8); usb_quickcam_i2c_add(&i2cbuff,0x58,x & 0xff); // y offset y = qcmax(1,y); usb_quickcam_i2c_add(&i2cbuff,0x59,y >> 8); usb_quickcam_i2c_add(&i2cbuff,0x5a,y & 0xff); // Set the real if (sensor_ctrl->mode) { sensor_ctrl->width=180; sensor_ctrl->height=148; } else { sensor_ctrl->width=356; sensor_ctrl->height=292; } // line length if (sensor_ctrl->mode) { if (isaweb(dev)) width=250; else width=360; /* 180 * 2 (CLK-DIV is 2) */ } else { if (isaweb(dev)) width=416; else width=712; /* 356 * 2 */ } usb_quickcam_i2c_add(&i2cbuff,0x52, width >> 8); usb_quickcam_i2c_add(&i2cbuff,0x53, width & 0xff); // field length (num lines) if (sensor_ctrl->mode) height=160; /* nearest of 148 = 10 * 16 */ else height=320; // 304; /* nearest of 292 = 19 * 16 */ usb_quickcam_i2c_add(&i2cbuff,0x61,height >> 8); usb_quickcam_i2c_add(&i2cbuff,0x62,height & 0xff); // usb_quickcam_i2c_add(&i2cbuff,0x25,0x02); return(usb_quickcam_i2c_send(dev,&i2cbuff,VV6410_ADDR)); } /* start grabbing */ static int vv6410_start(struct usb_device * dev, struct sensorctrl *sensor_ctrl) { struct quickcam_i2c i2cbuff; usb_quickcam_i2c_new(&i2cbuff); if (sensor_ctrl->mode) usb_quickcam_i2c_add(&i2cbuff,VV6410_CONTROL, 0xc0); // 0x80 else usb_quickcam_i2c_add(&i2cbuff,VV6410_CONTROL, 0x00); // 0x80 if (isaweb(dev)) { if (usb_quickcam_set1(dev, 0x1445, 1) < 0) { printk(KERN_ERR "vv6410_stop: Can't turn on led\n"); return (-2); } } // usb_quickcam_i2c_send(dev,&i2cbuff,VV6410_ADDR); // return(vv6410_set_window(dev,0,0,352, 288)); // Hacked! return(usb_quickcam_i2c_send(dev,&i2cbuff,VV6410_ADDR)); } /* stop grabbing */ static int vv6410_stop(struct usb_device * dev, struct sensorctrl *sensor_ctrl) { struct quickcam_i2c i2cbuff; if (isaweb(dev)) { if (usb_quickcam_set1(dev, 0x1445, 0) < 0) { printk(KERN_ERR "vv6410_stop: turn off led\n"); return (-2); } } usb_quickcam_i2c_new(&i2cbuff); if (sensor_ctrl->mode) usb_quickcam_i2c_add(&i2cbuff,VV6410_CONTROL,0xc2); // sleep mode. else usb_quickcam_i2c_add(&i2cbuff,VV6410_CONTROL,0x02); // sleep mode. if (usb_quickcam_i2c_send(dev,&i2cbuff,VV6410_ADDR) < 0) return (-2); return(0); } /* * initialise parameters for vv6410 sensor. * Just try to send the same commands as Windows Quickcam soft. */ static int vv6410_init(struct usb_device *dev, int mode, int *rgain, int *bgain, int *ggain, struct sensorctrl *sensor_ctrl) { struct quickcam_i2c i2cbuff; int line_length = mode?250:416;//415; if (mode) { sensor_ctrl->mode=2; // quater. sensor_ctrl->width = 180; sensor_ctrl->height = 148; } else { sensor_ctrl->mode=0; sensor_ctrl->width = 356; sensor_ctrl->height = 292; } if (*rgain<=0 || *rgain>255) *rgain=RGAIN_DEF; if (*bgain<=0 || *bgain>255) *bgain=BGAIN_DEF; if (*ggain<=0 || *ggain>255) *ggain=GGAIN_DEF; if (usb_quickcam_set1(dev, STV_REG23, 5) < 0) // was 5. goto error; if (!isaweb(dev)) { /* logitech quickcam web has 0x850 as idProduct */ if (usb_quickcam_set1(dev, 0x1446, 1) < 0) goto error; } if (usb_quickcam_set1(dev,STV_SCAN_RATE, 0x00) < 0) goto error; if (usb_quickcam_set1(dev, 0x1423, 0x04) < 0) goto error; if (usb_quickcam_set1(dev, STV_REG00, 0x1b) < 0) // 0x0b goto error; usb_quickcam_i2c_new(&i2cbuff); usb_quickcam_i2c_add(&i2cbuff,VV6410_CONTROL,0x04); // reset to defaults if (usb_quickcam_i2c_send(dev,&i2cbuff,VV6410_ADDR) < 0) { printk(KERN_ERR "usb_quickcam_i2c_out VV6410_CONTROL(0x04) failed\n"); goto error; } // CIF or QCIF and sleep. if (isaweb(dev)) usb_quickcam_i2c_add(&i2cbuff,VV6410_CONTROL,(mode?0xa2:0x02)); else usb_quickcam_i2c_add(&i2cbuff,VV6410_CONTROL,(mode?0xc2:0x02)); if (usb_quickcam_i2c_send(dev,&i2cbuff,VV6410_ADDR) < 0) { printk(KERN_ERR "usb_quickcam_i2c_out VV6410_CONTROL(0xc2) failed\n"); goto error; } usb_quickcam_i2c_add(&i2cbuff,VV6410_GAIN,0xfb); if (usb_quickcam_i2c_send(dev,&i2cbuff,VV6410_ADDR) < 0) { printk(KERN_ERR "usb_quickcam_i2c_out VV6410_GAIN(0x24) failed\n"); goto error; } if (usb_quickcam_set1(dev, STV_REG04, 0x07) < 0) goto error; if (usb_quickcam_set1(dev, STV_REG03, 0x45) < 0) goto error; /* set window size */ if (vv6410_set_window(dev,0,0,48,64,sensor_ctrl) < 0) { printk(KERN_ERR "vv6410_set_window failed"); goto error; } /* EXPERIMENTAL */ /* * line length default is 415 so it's the value we use to * calculate values for registers 0x20-0x21 * Ref. DS Pag. 67 */ usb_quickcam_i2c_add(&i2cbuff,0x20,mode? ((line_length-23)>>8):((line_length-51)>>8)); usb_quickcam_i2c_add(&i2cbuff,0x21, mode?((line_length-23)&0xff):((line_length-51)&0xff)); usb_quickcam_i2c_add(&i2cbuff,0x22,mode?0x00:0x01); //usb_quickcam_i2c_add(&i2cbuff,0x23,mode?0x9e:0x3e); usb_quickcam_i2c_add(&i2cbuff,0x23,mode?158:318&0xff); usb_quickcam_i2c_add(&i2cbuff,0x24,0xfa); // clock divisor. usb_quickcam_i2c_add(&i2cbuff,0x25,0x01); if (usb_quickcam_i2c_send(dev,&i2cbuff,VV6410_ADDR) < 0) { printk(KERN_ERR "usb_control_msg gain1 failed"); goto error; } /* if (isaweb(dev)) { //EXPERIMENTAL: dark/black pixel cancellation usb_quickcam_i2c_add(&i2cbuff,0x3e,0x01); usb_quickcam_i2c_add(&i2cbuff,0x72,0x01); if (usb_quickcam_i2c_send(dev,&i2cbuff,VV6410_ADDR) < 0) { printk(KERN_ERR "usb_control_msg dark/black pixel failed"); goto error; } } */ if (usb_quickcam_set1(dev, STV_REG01 , 0xb7) < 0) goto error; if (usb_quickcam_set1(dev, STV_REG02 , 0xa7) < 0) goto error; // setup usb_quickcam_i2c_add(&i2cbuff,0x11,0x18); // 0x18 or Jochen 0x40 usb_quickcam_i2c_add(&i2cbuff,0x14,0x55); // was 0x55 usb_quickcam_i2c_add(&i2cbuff,0x15,0x10); // 0x10 or Jochen:0x00 usb_quickcam_i2c_add(&i2cbuff,0x16,0x81); // Pre clock dividor. usb_quickcam_i2c_add(&i2cbuff,0x17,0x18); // they are reserved. usb_quickcam_i2c_add(&i2cbuff,0x18,0x00); usb_quickcam_i2c_add(&i2cbuff,0x77,0x5e); usb_quickcam_i2c_add(&i2cbuff,0x78,0x04);// 0x04 or Jochen:0x00 if (isaweb(dev)) usb_quickcam_i2c_add(&i2cbuff,0x79,0x11);//audio init if (usb_quickcam_i2c_send(dev,&i2cbuff,VV6410_ADDR) < 0) { printk(KERN_ERR "usb_control_msg setup failed"); goto error; } if (usb_quickcam_set2(dev, STV_ISO_SIZE, isaweb(dev)?1023:600) < 0) // 0x380|orig:600 goto error; if (usb_quickcam_set1(dev, STV_Y_CTRL, 1) < 0) goto error; if (usb_quickcam_set1(dev,STV_SCAN_RATE, mode?0x00:0x10) < 0) goto error; if (!isaweb(dev)) { /* logitech quickam web has 0x0850 as idProduct */ if (usb_quickcam_set1(dev, STV_X_CTRL, 0x14) < 0) goto error; } return 0; error: printk(KERN_ERR "vv6410_init failed\n"); return(-1); } static int vv6410_set_shutter(struct usb_device *dev, int sval, int xval, struct sensorctrl *sensor_ctrl) { struct quickcam_i2c i2cbuff; int ret; usb_quickcam_i2c_new(&i2cbuff); sval = sval * 2; usb_quickcam_i2c_add(&i2cbuff,0x23,sval&255); usb_quickcam_i2c_add(&i2cbuff,0x22,sval>>8); ret = usb_quickcam_i2c_send(dev,&i2cbuff,VV6410_ADDR); if (isaweb(dev)) usb_quickcam_set1(dev, 0x1704 , 0x01); return(ret); } static int vv6410_set_gains(struct usb_device *dev, int rgain, int bgain, int ggain) { struct quickcam_i2c i2cbuff; int gain; int ret; gain = (rgain+bgain+2*ggain)/4; if (gain>=0xF0) gain = 0xE0; gain = gain>>4; gain = 0xF0+gain; usb_quickcam_i2c_new(&i2cbuff); usb_quickcam_i2c_add(&i2cbuff,VV6410_GAIN,gain); ret = usb_quickcam_i2c_send(dev,&i2cbuff,VV6410_ADDR); if (isaweb(dev)) usb_quickcam_set1(dev, 0x1704 , 0x01); return(ret); } static int vv6410_set_size(struct usb_device *dev, int mode) { return 0; } void load_vv6410_mod(struct sensorctrl *sensor_ctrl) { sensor_ctrl->init = vv6410_init; sensor_ctrl->set_shutter = vv6410_set_shutter; sensor_ctrl->set_gains = vv6410_set_gains; sensor_ctrl->set_window = vv6410_set_window; sensor_ctrl->set_size = vv6410_set_size; sensor_ctrl->start = vv6410_start; sensor_ctrl->stop = vv6410_stop; } pwcbsd/qce-ga-0.40d/vv6410.h000644 000423 000000 00000002104 10551677522 015604 0ustar00luigiwheel000000 000000 #ifndef __LINUX_QUICKCAM_VV6410_H #define __LINUX_QUICKCAM_VV6410_H void load_vv6410_mod(struct sensorctrl *sensor_ctrl); // I2C Address #define VV6410_ADDR 0x20 #define VV6410_GAIN 0x24 // I2C Registers #define VV6410_IDENT 0x00 #define VV6410_STATUS 0x02 // Status #define VV6410_IMASK 0x04 // Interrupt mask #define VV6410_REG06 0x06 #define VV6410_REG08 0x08 #define VV6410_REG0A 0x0a #define VV6410_REG0C 0x0c #define VV6410_REG0E 0x0e #define VV6410_REG10 0x10 #define VV6410_ADC_BITS 0x12 #define VV6410_STARTX 0x57 #define VV6410_STARTY 0x59 #define VV6410_WIDTH 0x52 #define VV6410_HEIGHT 0x61 #define VV6410_INTEGRATE 0x1c #define VV6410_GAIN 0x24 #define VV6410_SHUTTERL 0x26 #define VV6410_SHUTTERH 0x28 #define VV6410_REG2A 0x2a #define VV6410_REG2C 0x2c #define VV6410_CONFIG 0x2e // Configuration #define VV6410_CONTROL 0x10 // Setup0 #define VV6410_REG32 0x32 #define VV6410_REG34 0x34 #define VV6410_REG36 0x36 #define VV6410_REG38 0x38 #endif pwcbsd/qce-ga-0.40d/yuv.c000644 000423 000000 00000033203 10551677522 015460 0ustar00luigiwheel000000 000000 /* * qce-ga, linux V4L driver for the Quickcam Express and Dexxa Quickcam * * yuv.c - converts color format RB24 to Y422 * * Copyright (c) 2001 Jean-Frederic Clere * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "quickcam.h" #include "helper.h" static short interp_table24[256][8] = { {0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,1,0,0,0,1,0,0},{0,1,0,0,0,1,-1,0}, {1,2,0,0,-1,2,-1,0},{1,2,0,0,-1,2,-2,0},{1,3,0,-1,-1,3,-2,0},{2,3,0,-1,-2,3,-2,0}, {2,4,0,-1,-2,4,-3,0},{2,5,1,-1,-2,4,-3,0},{2,5,1,-1,-3,5,-4,0},{3,6,1,-1,-3,5,-4,0}, {3,6,1,-2,-3,6,-5,0},{3,7,1,-2,-4,6,-5,-1},{4,7,1,-2,-4,7,-5,-1},{4,8,1,-2,-4,7,-6,-1}, {4,9,1,-2,-5,8,-6,-1},{5,9,1,-2,-5,8,-7,-1},{5,10,2,-3,-5,9,-7,-1},{5,10,2,-3,-6,9,-7,-1}, {5,11,2,-3,-6,10,-8,-1},{6,11,2,-3,-6,10,-8,-1},{6,12,2,-3,-7,11,-9,-1},{6,13,2,-3,-7,11,-9,-1}, {7,13,2,-4,-7,12,-10,-1},{7,14,2,-4,-8,12,-10,-2},{7,14,2,-4,-8,13,-10,-2},{8,15,3,-4,-8,13,-11,-2}, {8,15,3,-4,-9,14,-11,-2},{8,16,3,-4,-9,14,-12,-2},{8,17,3,-5,-9,15,-12,-2},{9,17,3,-5,-10,15,-12,-2}, {9,18,3,-5,-10,16,-13,-2},{9,18,3,-5,-10,16,-13,-2},{10,19,3,-5,-11,17,-14,-2},{10,19,3,-5,-11,17,-14,-2}, {10,20,4,-6,-11,18,-15,-2},{11,20,4,-6,-12,18,-15,-3},{11,21,4,-6,-12,19,-15,-3},{11,22,4,-6,-12,19,-16,-3}, {11,22,4,-6,-13,20,-16,-3},{12,23,4,-6,-13,20,-17,-3},{12,23,4,-7,-13,21,-17,-3},{12,24,4,-7,-14,21,-18,-3}, {13,24,5,-7,-14,22,-18,-3},{13,25,5,-7,-14,22,-18,-3},{13,26,5,-7,-15,23,-19,-3},{14,26,5,-7,-15,23,-19,-3}, {14,27,5,-8,-15,24,-20,-3},{14,27,5,-8,-16,24,-20,-3},{14,28,5,-8,-16,25,-20,-4},{15,28,5,-8,-16,25,-21,-4}, {15,29,5,-8,-17,26,-21,-4},{15,30,6,-8,-17,26,-22,-4},{16,30,6,-9,-17,27,-22,-4},{16,31,6,-9,-18,27,-23,-4}, {16,31,6,-9,-18,28,-23,-4},{17,32,6,-9,-18,28,-23,-4},{17,32,6,-9,-19,29,-24,-4},{17,33,6,-9,-19,29,-24,-4}, {17,34,6,-10,-19,30,-25,-4},{18,34,6,-10,-20,30,-25,-4},{18,35,7,-10,-20,31,-25,-5},{18,35,7,-10,-20,31,-26,-5}, {19,36,7,-10,-21,32,-26,-5},{19,36,7,-10,-21,32,-27,-5},{19,37,7,-11,-21,33,-27,-5},{20,37,7,-11,-22,33,-28,-5}, {20,38,7,-11,-22,34,-28,-5},{20,39,7,-11,-22,34,-28,-5},{20,39,7,-11,-23,35,-29,-5},{21,40,8,-11,-23,35,-29,-5}, {21,40,8,-12,-23,36,-30,-5},{21,41,8,-12,-24,36,-30,-5},{22,41,8,-12,-24,37,-30,-6},{22,42,8,-12,-24,37,-31,-6}, {22,43,8,-12,-25,38,-31,-6},{23,43,8,-12,-25,38,-32,-6},{23,44,8,-13,-25,39,-32,-6},{23,44,9,-13,-26,39,-33,-6}, {23,45,9,-13,-26,40,-33,-6},{24,45,9,-13,-26,40,-33,-6},{24,46,9,-13,-27,41,-34,-6},{24,47,9,-14,-27,41,-34,-6}, {25,47,9,-14,-27,42,-35,-6},{25,48,9,-14,-28,42,-35,-6},{25,48,9,-14,-28,43,-36,-6},{26,49,9,-14,-28,43,-36,-7}, {26,49,10,-14,-29,44,-36,-7},{26,50,10,-15,-29,44,-37,-7},{26,51,10,-15,-29,45,-37,-7},{27,51,10,-15,-30,45,-38,-7}, {27,52,10,-15,-30,46,-38,-7},{27,52,10,-15,-30,46,-38,-7},{28,53,10,-15,-31,47,-39,-7},{28,53,10,-16,-31,47,-39,-7}, {28,54,10,-16,-31,48,-40,-7},{29,54,11,-16,-32,48,-40,-7},{29,55,11,-16,-32,49,-41,-7},{29,56,11,-16,-32,49,-41,-8}, {29,56,11,-16,-33,50,-41,-8},{30,57,11,-17,-33,50,-42,-8},{30,57,11,-17,-33,51,-42,-8},{30,58,11,-17,-34,51,-43,-8}, {31,58,11,-17,-34,52,-43,-8},{31,59,11,-17,-34,52,-43,-8},{31,60,12,-17,-35,53,-44,-8},{31,60,12,-18,-35,53,-44,-8}, {32,61,12,-18,-35,54,-45,-8},{32,61,12,-18,-36,54,-45,-8},{32,62,12,-18,-36,55,-46,-8},{33,62,12,-18,-36,55,-46,-9}, {33,63,12,-18,-37,56,-46,-9},{33,64,12,-19,-37,56,-47,-9},{34,64,12,-19,-37,57,-47,-9},{34,65,13,-19,-38,57,-48,-9}, {34,65,13,-19,-38,58,-48,-9},{34,66,13,-19,-38,58,-48,-9},{35,66,13,-19,-39,59,-49,-9},{35,67,13,-20,-39,59,-49,-9}, {35,68,13,-20,-39,60,-50,-9},{36,68,13,-20,-40,60,-50,-9},{36,69,13,-20,-40,61,-51,-9},{36,69,14,-20,-40,61,-51,-9}, {37,70,14,-20,-41,62,-51,-10},{37,70,14,-21,-41,62,-52,-10},{37,71,14,-21,-41,63,-52,-10},{37,72,14,-21,-42,63,-53,-10}, {38,72,14,-21,-42,64,-53,-10},{38,73,14,-21,-42,64,-54,-10},{38,73,14,-21,-43,65,-54,-10},{39,74,14,-22,-43,65,-54,-10}, {39,74,15,-22,-43,66,-55,-10},{39,75,15,-22,-44,66,-55,-10},{40,75,15,-22,-44,67,-56,-10},{40,76,15,-22,-44,67,-56,-10}, {40,77,15,-22,-45,68,-56,-11},{40,77,15,-23,-45,68,-57,-11},{41,78,15,-23,-45,69,-57,-11},{41,78,15,-23,-46,69,-58,-11}, {41,79,15,-23,-46,70,-58,-11},{42,79,16,-23,-46,70,-59,-11},{42,80,16,-23,-47,71,-59,-11},{42,81,16,-24,-47,71,-59,-11}, {43,81,16,-24,-47,72,-60,-11},{43,82,16,-24,-48,72,-60,-11},{43,82,16,-24,-48,73,-61,-11},{43,83,16,-24,-48,73,-61,-11}, {44,83,16,-24,-49,74,-61,-12},{44,84,16,-25,-49,74,-62,-12},{44,85,17,-25,-49,75,-62,-12},{45,85,17,-25,-50,75,-63,-12}, {45,86,17,-25,-50,76,-63,-12},{45,86,17,-25,-50,76,-64,-12},{46,87,17,-25,-51,77,-64,-12},{46,87,17,-26,-51,77,-64,-12}, {46,88,17,-26,-51,78,-65,-12},{46,89,17,-26,-52,78,-65,-12},{47,89,18,-26,-52,79,-66,-12},{47,90,18,-26,-52,79,-66,-12}, {47,90,18,-26,-53,80,-66,-13},{48,91,18,-27,-53,80,-67,-13},{48,91,18,-27,-53,81,-67,-13},{48,92,18,-27,-54,81,-68,-13}, {49,92,18,-27,-54,82,-68,-13},{49,93,18,-27,-54,82,-69,-13},{49,94,18,-28,-54,83,-69,-13},{49,94,19,-28,-55,83,-69,-13}, {50,95,19,-28,-55,84,-70,-13},{50,95,19,-28,-55,84,-70,-13},{50,96,19,-28,-56,85,-71,-13},{51,96,19,-28,-56,85,-71,-13}, {51,97,19,-29,-56,86,-72,-13},{51,98,19,-29,-57,86,-72,-14},{52,98,19,-29,-57,87,-72,-14},{52,99,19,-29,-57,87,-73,-14}, {52,99,20,-29,-58,88,-73,-14},{52,100,20,-29,-58,88,-74,-14},{53,100,20,-30,-58,89,-74,-14},{53,101,20,-30,-59,89,-74,-14}, {53,102,20,-30,-59,90,-75,-14},{54,102,20,-30,-59,90,-75,-14},{54,103,20,-30,-60,91,-76,-14},{54,103,20,-30,-60,91,-76,-14}, {55,104,20,-31,-60,92,-77,-14},{55,104,21,-31,-61,92,-77,-15},{55,105,21,-31,-61,93,-77,-15},{55,106,21,-31,-61,93,-78,-15}, {56,106,21,-31,-62,94,-78,-15},{56,107,21,-31,-62,94,-79,-15},{56,107,21,-32,-62,95,-79,-15},{57,108,21,-32,-63,95,-79,-15}, {57,108,21,-32,-63,96,-80,-15},{57,109,22,-32,-63,96,-80,-15},{58,109,22,-32,-64,97,-81,-15},{58,110,22,-32,-64,97,-81,-15}, {58,111,22,-33,-64,98,-82,-15},{58,111,22,-33,-65,98,-82,-16},{59,112,22,-33,-65,99,-82,-16},{59,112,22,-33,-65,99,-83,-16}, {59,113,22,-33,-66,100,-83,-16},{60,113,22,-33,-66,100,-84,-16},{60,114,23,-34,-66,101,-84,-16},{60,115,23,-34,-67,101,-84,-16}, {60,115,23,-34,-67,102,-85,-16},{61,116,23,-34,-67,102,-85,-16},{61,116,23,-34,-68,103,-86,-16},{61,117,23,-34,-68,103,-86,-16}, {62,117,23,-35,-68,104,-87,-16},{62,118,23,-35,-69,104,-87,-16},{62,119,23,-35,-69,105,-87,-17},{63,119,24,-35,-69,105,-88,-17}, {63,120,24,-35,-70,106,-88,-17},{63,120,24,-35,-70,106,-89,-17},{63,121,24,-36,-70,107,-89,-17},{64,121,24,-36,-71,107,-90,-17}, {64,122,24,-36,-71,108,-90,-17},{64,123,24,-36,-71,108,-90,-17},{65,123,24,-36,-72,109,-91,-17},{65,124,24,-36,-72,109,-91,-17}, {65,124,25,-37,-72,110,-92,-17},{66,125,25,-37,-73,110,-92,-17},{66,125,25,-37,-73,111,-92,-18},{66,126,25,-37,-73,111,-93,-18}, {66,127,25,-37,-74,112,-93,-18},{67,127,25,-37,-74,112,-94,-18},{67,128,25,-38,-74,113,-94,-18},{67,128,25,-38,-75,113,-95,-18}, {68,129,25,-38,-75,114,-95,-18},{68,129,26,-38,-75,114,-95,-18},{68,130,26,-38,-76,115,-96,-18},{69,130,26,-38,-76,115,-96,-18}, {69,131,26,-39,-76,116,-97,-18},{69,132,26,-39,-77,116,-97,-18},{69,132,26,-39,-77,117,-97,-19},{70,133,26,-39,-77,117,-98,-19}, {70,133,26,-39,-78,118,-98,-19},{70,134,27,-39,-78,118,-99,-19},{71,134,27,-40,-78,119,-99,-19},{71,135,27,-40,-79,119,-100,-19}, {71,136,27,-40,-79,120,-100,-19},{72,136,27,-40,-79,120,-100,-19},{72,137,27,-40,-80,121,-101,-19},{72,137,27,-40,-80,121,-101,-19}, {72,138,27,-41,-80,122,-102,-19},{73,138,27,-41,-81,122,-102,-19},{73,139,28,-41,-81,123,-103,-19},{73,140,28,-41,-81,123,-103,-20}, {74,140,28,-41,-82,124,-103,-20},{74,141,28,-42,-82,124,-104,-20},{74,141,28,-42,-82,125,-104,-20},{75,142,28,-42,-83,125,-105,-20}, {75,142,28,-42,-83,126,-105,-20},{75,143,28,-42,-83,126,-105,-20},{75,144,28,-42,-84,127,-106,-20},{76,144,29,-43,-84,127,-106,-20}}; /* * Supported format in list. */ static struct qc_palette_list plist[] = { { VIDEO_PALETTE_GREY, "GREY", 1 }, { VIDEO_PALETTE_HI240, "HI240", 0 }, { VIDEO_PALETTE_RGB565, "RGB565", 1 }, { VIDEO_PALETTE_RGB24, "RGB24", 1 }, { VIDEO_PALETTE_RGB32, "RGB32", 1 }, { VIDEO_PALETTE_RGB555, "RGB555", 1 }, { VIDEO_PALETTE_YUV422, "YUV422", 1 }, // to be tested. { VIDEO_PALETTE_YUYV, "YUYV", 0 }, // Not supported yet. { VIDEO_PALETTE_UYVY, "UYVY", 0 }, // Not supported yet. { VIDEO_PALETTE_YUV420, "YUV420", 0 }, // Not supported yet. { VIDEO_PALETTE_YUV411, "YUV411", 0 }, // Not supported Ok. { VIDEO_PALETTE_RAW, "RAW", 0 }, { VIDEO_PALETTE_YUV422P, "YUV422P", 1 }, { VIDEO_PALETTE_YUV411P, "YUV411P", 1 }, { VIDEO_PALETTE_YUV420P, "YUV420P", 0 }, // Not supported yet. { VIDEO_PALETTE_YUV410P, "YUV410P", 0 }, // Not supported yet. { -1, NULL } }; /* * Convert to planar formats * The input is an YCbYCr format. * Input: len : 2/3 maxi. * | YUYV | free | * | 2/3 | 1/3 | * 1th conversion: * | YY | free | U|V | * | 1/3 | 1/3 | 1/3 | * 2d conversion: * | YY | U | V | free | * | 1/3 | 1/6|1/6 | 1/3 | * That the Y422P conversion. */ static void quickcam_convert_planar(struct quickcam_frame *frame, int format) { unsigned char *ptr; int n = 0, l = 0, i; unsigned char *rgb24frame = frame->data; int length = frame->scanlength; unsigned char *cr; unsigned char *cb; unsigned char *crptr, *cbptr; int mode = 0; l = length/2; switch(format) { case VIDEO_PALETTE_YUV411P: n = length/8; mode = 1; break; case VIDEO_PALETTE_YUV422P: n = length/4; break; } ptr = rgb24frame; crptr = &rgb24frame[length]; cr = crptr; cbptr = &rgb24frame[length+n]; cb = cbptr; //printk("quickcam_convert_planar: %d (%d + 2 * %d)\n",length, l, n); /* separate Y , U and V */ for (i=0;iscanlength = length/2+ (2 * n); } /* * Convert the RGB24 image to YUV422. * return the size of the converted frame */ void quickcam_convert_image(struct quickcam_frame *frame, int format) { int i,l; short Y; short U; short V; unsigned char *ptr; unsigned short w; unsigned char *rgb24frame = frame->data; int length = frame->scanlength; //printk("quickcam_convert_image: %d format: %d\n",length,format); ptr = rgb24frame; l = 0; /* we need to convert in reverse so as to not overwrite ourselves */ if (format == VIDEO_PALETTE_RGB32) for (ptr=ptr+length*4/3,i=length-3;i>=0;i=i-3) { *--ptr = 0; *--ptr = (unsigned char) rgb24frame[i + 2]; *--ptr = (unsigned char) rgb24frame[i + 1]; *--ptr = (unsigned char) rgb24frame[i + 0]; l += 4; } else for (i=0;i> 3))); *ptr++ = (unsigned char) (w & 0xFF); *ptr++ = (unsigned char) (w >> 8); l += 2; break; case VIDEO_PALETTE_RGB555: w = ((unsigned short) ((unsigned short) (rgb24frame[i + 2] & 0xf8) << (10 - 3)) | ((unsigned short) (rgb24frame[i + 1] & 0xf8) << (5 - 3)) | ((unsigned short) (rgb24frame[i + 0] >> 3))); *ptr++ = (unsigned char) (w & 0xFF); *ptr++ = (unsigned char) (w >> 8); l += 2; break; default: *ptr++ = (219 * Y)/255 + 16; l++; *ptr = (112 * U)/127 + 128; /* Cb */ ptr++; l++; *ptr = (112 * V)/127 + 128; /* Cr */ ptr++; l++; } } frame->scanlength = l; if (format= 0; i++) { if(plist[i].num == format && plist[i].supported) return(0); } return(-EINVAL); } /* Return the format name */ char *FormatName(int format) { int i; for(i = 0; plist[i].num >= 0; i++) { if(plist[i].num == format) return(plist[i].name); } return("Unknown"); } pwcbsd/qce-ga-0.40d/testquickcam/.cvsignore000644 000423 000000 00000000014 10546676144 021163 0ustar00luigiwheel000000 000000 testquickcampwcbsd/qce-ga-0.40d/testquickcam/makefile000644 000423 000000 00000001036 10546676144 020670 0ustar00luigiwheel000000 000000 #LINUX_DIR = /usr/src/linux # #MODULE_INC= -I$(LINUX_DIR)/include -include $(LINUX_DIR)/include/linux/config.h #MODULE_DEFS:= $(shell [ -f /usr/include/linux/modversions.h ] && echo -DEXPORT_SYMTAB -DMODVERSIONS -include /usr/include/linux/modversions.h ) #MODULE_OPT = -O3 #MODULE_OPT_WARN = -Wall -Wstrict-prototypes -fomit-frame-pointer -pipe #MODULE_CFLAGS = $(MODULE_INC) $(MODULE_OPT) $(MODULE_OPT_WARN) $(MODULE_DEFS) all: testquickcam.o clean: rm testquickcam -f testquickcam.o: testquickcam.c .c.o: $(CC) -o testquickcam $< pwcbsd/qce-ga-0.40d/testquickcam/testquickcam.c000644 000423 000000 00000012461 10546676144 022035 0ustar00luigiwheel000000 000000 /* * Test quickcam: Logitech Quickcam Express Video Camera driver test tool. * Copyright (C) 2001 Nikolas Zimmermann * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * * */ #include #include #include #include #include #include #include "testquickcam.h" #define VERSION "$Id: testquickcam.c,v 1.1 2007/01/02 21:32:17 luigi Exp $" int open_camera(const char *devicename) { device_fd = open(devicename, O_RDWR); if(device_fd <= 0) { printf("Device %s couldn't be opened\n", devicename); return 0; } return 1; } void close_camera(void) { close(device_fd); } void get_camera_info(void) { ioctl(device_fd, VIDIOCGCAP, &vidcap); ioctl(device_fd, VIDIOCGWIN, &vidwin); ioctl(device_fd, VIDIOCGPICT, &vidpic); vidwin.clips = vidclips; vidwin.clipcount = 0; } void print_camera_info(void) { printf(" *** Camera Info ***\n"); printf("Name: %s\n", vidcap.name); printf("Type: %d\n", vidcap.type); printf("Minimum Width: %d\n", vidcap.minwidth); printf("Maximum Width: %d\n", vidcap.maxwidth); printf("Minimum Height: %d\n", vidcap.minheight); printf("Maximum Height: %d\n", vidcap.maxheight); printf("X: %d\n", vidwin.x); printf("Y: %d\n", vidwin.y); printf("Width: %d\n", vidwin.width); printf("Height: %d\n", vidwin.height); printf("Depth: %d\n", vidpic.depth); if(vidcap.type & VID_TYPE_MONOCHROME) printf("Color false\n"); else printf("Color true\n"); printf("Version: %s\n", VERSION); } static void hexdump_data(const unsigned char *data, int len) { const int bytes_per_line = 32; char tmp[128]; int i = 0, k = 0; for(i = 0; len > 0; i++, len--) { if(i > 0 && ((i % bytes_per_line) == 0)) { printf("%s\n", tmp); k = 0; } if ((i % bytes_per_line) == 0) k += sprintf(&tmp[k], "[%04x]: ", i); k += sprintf(&tmp[k], "%02x ", data[i]); } if (k > 0) printf("%s\n", tmp); } void read_test(int quiet) { unsigned char *buffer = malloc(vidcap.maxwidth * vidcap.maxheight * 3); int len = 0; len = read(device_fd, buffer, vidcap.maxwidth * vidcap.maxheight * 3); if(!quiet) { printf(" *** read() test ***\n"); printf("Read length: %d\n", len); printf("Raw data: \n\n"); hexdump_data(buffer, len); } } void mmap_test(int quiet) { struct video_mbuf vidbuf; struct video_mmap vm; int numframe = 0; unsigned char *buffer; ioctl(device_fd, VIDIOCGMBUF, &vidbuf); buffer = mmap(NULL, vidbuf.size, PROT_READ, MAP_SHARED, device_fd, 0); vm.format = VIDEO_PALETTE_RGB24; vm.frame = 0; vm.width = 352; vm.height = 288; ioctl(device_fd, VIDIOCMCAPTURE, &vm); vm.frame = 1; vm.width = 352; vm.height = 288; ioctl(device_fd, VIDIOCMCAPTURE, &vm); ioctl(device_fd, VIDIOCSYNC, &numframe); numframe = 1; ioctl(device_fd, VIDIOCSYNC, &numframe); if(!quiet) { printf(" *** mmap() test ***\n"); printf("Read length: %d\n", vidbuf.size); printf("Raw data: \n\n"); hexdump_data(buffer, vidbuf.size); } } void read_loop(void) { int loop = 0; while(1) { loop++; read_test(1); printf("*** Read frames: %d times!\n", loop); } } int main(int argc, char *argv[]) { char *switchtwo = ""; char *switchthree = ""; char *switchfour = ""; if(argc == 1) { printf(" *** Usage ***\n"); printf("./testquickcam DEVICE [ -r | -m | -l ]\n\n"); printf(" -r reads one frame via read() from the camera\n"); printf(" -m reads one frame via mmap() from the camera\n"); printf(" -l read() loop...good for debugging gain etc \n"); exit(1); } if(argc >= 3) switchtwo = argv[2]; if(argc >= 4) switchthree = argv[3]; if(open_camera(argv[1]) == 1) { get_camera_info(); print_camera_info(); if(strcmp(switchtwo, "-r") == 0) read_test(0); if(strcmp(switchtwo, "-m") == 0) mmap_test(0); if(strcmp(switchtwo, "-l") == 0) read_loop(); if(strcmp(switchthree, "-r") == 0) read_test(0); if(strcmp(switchthree, "-m") == 0) mmap_test(0); if(strcmp(switchthree, "-l") == 0) read_loop(); if(strcmp(switchfour, "-r") == 0) read_test(0); if(strcmp(switchfour, "-m") == 0) mmap_test(0); if(strcmp(switchfour, "-l") == 0) read_loop(); close_camera(); } exit(1); } pwcbsd/qce-ga-0.40d/testquickcam/testquickcam.h000644 000423 000000 00000000534 10546676144 022040 0ustar00luigiwheel000000 000000 /* * Logitech Quickcam Express Video Camera driver * * tiny test program * * (C) Copyright 2001 Nikolas Zimmermann */ #include static int device_fd; static struct video_capability vidcap; static struct video_window vidwin; static struct video_picture vidpic; static struct video_clip vidclips[32]; pwcbsd/spca5xx-20060402/CHANGELOG000644 000423 000000 00000132753 10546676140 016322 0ustar00luigiwheel000000 000000 This is the changelog for the SPCA5xx usb camera driver. Newest entries appear at the top of this file. /*Experimental spca5xx work based on spca50x module */ Release 0;57.11 Release 0;57.11rc2 Improve spc200nc spc300nc detection, need to be run twice to get a stream Add Samsung Pleomax SAMPWC3800N VGA 0x0ac8:0x301b HV7131d Release 0;57.11rc1 Remove old bug when palette and size did not match in mmap:( Release 0.57.10 Release 0.57.10rc5 Add Sonix 0x0c45:0x6019 Sn9c101+Ov7630 Add Sonix 0x0c45:0x6024 Sn9c102+Tas5130 Thanks Angelo Giraldi for investigation and testing :) Release 0.57.10rc4 Add SPC300NC preliminary 0x0471:0x0326 Vimicro vc0305 need test from Joachim Franeck Release 0.57.10rc3 Add pac207 autoexposure with Thomas Kaiser snoop comments that give better exposure in low light but the cost is the frame rate:( Release 0.57.10rc2 Add SPC600NC from Philips 0x0471:0x0327 Sonix sn9c105+MI0360 Release 0.57.10rc1 Add sn9c101+tas5110D Thanks Haplo for testing Release 0.57.09 Change spcadecoder yuv420p jpeg.4.1.1 to avoid pixels saturation test by Thomas Schorpp thanks Decrease the delay 150ms in the ov7630c detection routine, test by Stephane Rosi thanks. Release 0.57.09rc5 Add Philips SPC 700NC Sonix sn9c105 + MI0360 0x0471:0x0328 Thanks Juergen Lueters from Intranet Engineering GmbH for the spc700nc donation :) Adjust sn9cxx setting for the MI0360 sensor. Quantization FIX on aiptek problem, hope the last one. Release 0.57.09rc4 Add code for the 320x240 mode of zc030x + ov7630c untested Release 0.57.09rc3 Add Philips SPC 200 NC Vimicro VC0305 0x0471:0x0325 Thanks Jan Sigurd Refvik for testing. Release 0.57.09rc2 Add Creative Vista Plus 0x041e:0x4028 Pac207 chipset this is a work of Waldemar Celuch :) Release 0.57.09rc1 Add OV7630C detection routine to thz zc030x chips Add Empress PC390 webcam based on OV7630C sensor + vm0302 Thanks Stéphane Rosi snoop and testing :) Add Genius VideoCam NB 0x0c45:0x6001 Tas5110 Thanks Andrea Dangel for the patch and maybe others i forget Release 0.57.08 FIX some quantization problem with the spca500 spca504 spca533 chips Thanks Carlos Martínez Lozano to point the bug Release 0.57.08rc4 Add Chuntex (CTX) M730V TFT-Display UsbId 0x0698:0x2003 Thanks Thilo Martens for the patch :) the patch Release 0.57.08rc3 Add Aiptek Slim 3000F 0x08ca:0x2020 Thanks Renaud for patch and testing :) Release 0.57.08rc2 Fix problem when the webcam report palette 0 if VIDIOCSPICT is not used Add Logitech QC IM 0x046d:0x08a6 zc0302 + HV7131R Thanks Brian G. Maddox :) Release 0.57.08rc1 Vimicro vm303 0x0ac8:0x303b add new sensor detection and sensor setting use spca50x->chip_revision to switch the change. This sensor look like a MI0360 or PB0330 with others registers setting. thanks Juergen Lueters from Intranet Engineering GmbH for the PC Camera:) Release 0.57.07 Add ioctl() private to set qtable index time_interval between frame acquisition auto exposure on off spca561 spca504c zc030x chipsets Add a time_interval in the interrupt handler, allow change of the frame rate for each webcam between the available full frame rate to a slower one. Fix some old bugs in process context and the wait queue. VIDIOCSYNCHRO should wait for the frame instead of the spca5xx general wait queue only used by the read() method. -DSPCA5XX_ENABLE_REGISTERPLAY can be used for all spca5xx based webcam becare this option is Dangerous you can break your chipset (allow registers setting on the fly with sysfs !! Release 0.57.06rc6 Improve sensor detection for the zc03xx chipset Add quality index for the jpeg webcam Set default Index to 2 for all zc030x Change initialize of qtable according the new index field Change brightness contrast setting for the zc030x webcams set autobrightness module param for the zc030x /spca561/Etoms webcam Release 0.57.06rc5 FIX Genius Look 312p initialize correctly Thanks Cristophe and all from Interireur.gouv.fr:) FIX hv7131r sensor drift on frame rate Thanks Simon Morlat for testing (linphone) :) Release 0.57.06rc4 Add zx3xx routine for a sensor PB0330 or MI0330 8b <- dc Reduce the FrameBuffer to two. Look like footprint decrease without loss of performance :) Release 0.57.06rc3 Add Genius GF112 0x093a:0x2470 PAC207-BCA thanks ???? at land.ru. Release 0.57.06rc2 Add Mercury Cyberpix S550V 0x0733:0x3281 thanks Jross for testing :) Release 0.57.06rc1 Add decompression for the pac207 this a work of Bertrick Sikken Thomas Kaiser and me :) use with modprobe spca5xx compress=1 Release 0.57.05 FIX compilation problem in 2.4.x thanks Sven Lindberg :) Release 0.57.04 final 2005:11:01 Release 0.57.04rc6 FIX a small bug introduce by me in the sonix decompressor update the readme Release 0.57.04rc5 FIX from Bretrik Stikken sonix decompressor Thanks Thomas Kaiser pixart_preprocess now handle all pixels :) remove old code for the SAA7113 not need correct all sensor unknow with SENSOR_INTERNAL move all spca50x i2c code in spca506.h as it is only used by this chips remove autoexposure and the Bottom Half un need and crash the kernel remove all un need parameters : ccd, cams, osd, and so on Release 0.57.04rc4 Add 0x093a:0x2460 Pac207 Qtec Webcam Thanks Gerard Klaver Add 0x093a:0x2471 Pac207 Genius Videocam GE111 Thanks Tomas Groth and Unknow from IRC channel:) Add Vimicro 0x0ac8:0x303b Thanks Fionn Brehens and Will Crozier :) Release 0.57.04rc3 FIX Endpoint bug in the pixart Pac207 Add control brightness pac207 Add control contrast pac207 Release 0.57.04rc2 Add Pixart Pac207 0x093a:0x2468 This is a work of Thomas Kaiser :) Release 0.57.04rc1 FIX compilation with kernel > 2.6.14 thanks Kel Modderman and all :) Release 0.57.03 2005/10/01 FIX PC Cam350 offset data wrong value Add Creative Pccam350 0x041e:0x4012 thanks JULIANO CESAR CAMARGO for snoop and test:) 0.57.03rc7 FIX bug in VIOCSPICT only change format if need Remove a lock reduce latencies to serve the ioctl Add Smile International 0x0497:0xc001 spca501c webcam Thanks Rex Tsai for the patch :) 0.57.03rc6 spca504a spca504b spca504c spca533 and spca536 go out instead sp5xxfw2.h and sp5xxfw2.dat do the job sp5xxgfw2 is tested with a pocket DVII spca533 a Gsmart mini2 spca504a a aiptek 1.3 spca504a and Maxell 1.3 spca504b I did not have a pccma600 to test spca504c and a spca536 webcam if someone can test an report thanks:) sp5xxfw2 is now set with the new camlib API look dummy_cam.h Add JVC GC-A50 spca504 fw2 0x04f1:0x1001 thanks Tobias Wolf for testing :) 0.57.03rc5 Add Labtec Webcam Notebook 0x046d:0x08aa thanks Martin Weber for test and patch :) FIX a typo in zc03xx.h rapported by Gabriel Sere thanks :) 0.57.03rc4 Add Logitech Notebook Deluxe Zc0302 0x046d:0x08a9 thanks Anthony B. Coates for testing:) FIX the i2c routine for the sn9cxxx chips need a small delay Thanks Derzu for test and patch :) 0.57.03rc3 FIX Bug with the new spca561 decompressor add new stream S561 instead of GBRG Etoms chips rework Add Led feature for the sn9c101 sn9c102 chips 0.57.03rc2 FIX some typo for the 0x60fc Lic-300 Sensor should be set to HV7131R spca50x->i2c_base and spca50x->i2c_ctrl_reg set with the good value for the Sonix chips Add preliminary sn9c102+pas202 0x0c45:6028 BTC PC380 need gamma=5 0.57.03rc1 Rewrite the bayer decoder with some usefull Macro :) Add Fix a strange bug with UV in bayer decoder :( Release 0.57.02 FIX forget to set the zc305b sorry Otavio Salvadore Release 0.57.01 0.57.01rc6 Add some gain to the MI0360 need to be adjusted FIX bug in the sonix_config() with jpeg and CIF size The SN9C102P SN9C105 SN9C120 familly is know as BRIDGE SN9CXXX :) and SN9C101 SN9C102 SN9C103 as BRIDGE SONIX customid field now identify the chips 0.57.01rc5 FIX VGA sn9c105 + MI0360 Add Polaroid Ion80 spca504b 0x0546:0x3191 thanks Michel Memeteau for snoop and test 0.57.01rc4 Add MI0360 sensor sn9c105 + MI0360 is now working in 320x240 full size did not work ATM 0.57.01rc3 Add sonix sn9c120 HV7131R 0x0c45:0x613c Add sn9c120 MI0360 0x0c45:0x6130 preliminary not working Add sn9c105 MI0360 0x0c45:0x60c0 " Add sn9c105 HV7131R 0x0c45:0x60fc should work ? 0.57.01rc2 Add sonix Sweex Tas5110 0x0c45:0x6005 thanks Gerard Klaver Add sonix Lic-200 Hv7131r 0x0c45:602d thanks Leonardo R. Chrudina for snoop and testing :) 0.57.01rc1 Add spca561 decompressor from Andrzej Szombierski thanks Andrzej very good works :) Add Mustek MDC4000 Spca533 0x055f:0x0630 thanks Gerard Delafond :) Add Logitech Communicate STX zc030x 0x046d:0x08ad thanks Birger Koblitz for patch and testing Add Vimicro VC0305 0x0ac8:0x305b Thanks Otavio Salvador for feedback patch and testing :) Release 0.57.00 Change in this release: 0.57.00 final Add 0x0458:0x7006 Genius Dsc 1.3 spca504B-P3 thanks Dmitriy Zasiadiko Kharkiv (Ukraine) for patch and opening the cam with a screw driver :) 0.57.00rc7 Rewrite the jpeg decoder Each webcam have is own rgb table jpeg decode now is per webcam jpeg quantization table is set on probe Connect the Mars-Semi jpeg decoding Rewrite gamma and RGB table per webcams 0.57.00rc6 Add PRELIMINARY Mars-Semi Pcam works from Brad :) Bridge MR97311 Sensor MT9M001 jpeg camera 0x093a:0x050f 0.57.00rc5 reverse sn9c102 + ov7630 brightness until Menezes test :( FIX interrupt_sleep_on() deprecated in 2.6.x kernel lock policy rewrite brr :) add one tasklet per device for multi-webcams purpose lot of code clean up add exposure for the spca561 rev 012a add a field chip_revision to the main structure spca50x 0.57.00rc4 Add experimental brightness for the ov7630 Add experimental sonix sn9c102 ov7630 0x0c45:0x602c Add experimental sonix sn9c101 ov7630 0x0c45:0x602e thanks Carlos Eduardo Dantas de Menezes for snoop and testing :) 0.57.00rc3 A lot of change in the spca561 lib allow contrast brightness Add Logitec QC Notebook 0x046d:0x08ae Thanks Thomas Kaiser for the patch Add Mustek WCam300AN 0x055f:0xd004 Thanks Leslie Katz for the INF file :) Add Labtec Webcam Plus 0x046d:0x092b thanks Andrei Soukharev for the feedback 0.57.00rc2 *Switch the spca561 with new webcam API Improve colors for the creative Vista 0x403b tested with Creative vista 0x403b Ic50c 0x0561 Genius V2 0x0561 Hope the Logitech Express Echl2 works too :) *Switch the Etoms with the new webcam API correct return value tested with Sangha Et61x151+Pas106 Generic+Tas5130c 0.57.00rc1 *Switch the sonix with the new webcam API Improve the sonix PAS106 frame rate is now upto 22 fp/s Improve sonix TAS5130c Exposure Add autobrightness autoexposure for the sonix chips in the todo list test with Trust spaceCam120 SIF and shangha Tas5130c VGA Release 0.56.03 Changes in this release: 0.56.03rc5 Many thanks for Thomas Kaiser for the Creative NX patch code test snoop :)) Connect the new functions in the core driver for the zc030x only as a experimental test :) Create spca5xx_restartMode() Create spca5xx_setMode() FIX a small bug in spca5xx_getDefaultMode Create spca5xx_setFrameDecoder() Create spca5xx_initDecoder() new kernel lock policy tested on an Dual Athlon2.2 M SMP box seem strong as a rock :) 0.56.03rc4 create spca5xx_getDefaultMode() set webcam default mode (working on zc030x actually) Modify spca50x_configure according that change 0.56.03rc3 Add Creative NX usbid 0x041e:0x401c Add Creative Instant 0x041e:0x4034 Add Creative Instant 0x041e:0x4035 FIX qtable index for some zc030x webcam (should make Dr.beco and Ulisses Happy) Change Webcam API experiment on the zc030x chips Create a Dummy_cam.h the minimal set of webcam functions re-visit kernel locking policy :) remove all locks in_interrupt() context (brr historical design need some toilet). remove deferred work by timer on close remove PENDING DEALLOC remove timer 0.56.03rc2 Improve stability Add exposure for the sn9c102p coupled with brightness rework disconnect open close FIX Ooops or deadlock on disconnect remove not need timer function on disconnect. open should quit with an error now if somethings goes wrong tested with Suse 2.4.21-199 athlon Vanilla 2.4.25 Vanilla 2.6.9 Add a new function spca5xx_kill_isoc in case submit fail on open Release 0.56.03rc1 Add Concord 3045 0x0733:0x3261 spca536a Thanks Adam Majewski for feedback and testing :) Add Mercury 2.1Mp 0x0733:0x1314 spca533a Thanks Pawel Kotynia for the patch :) Release 0.56.02 Changes in this release: 24/04/2005: Add Bridge Sonix sn9c102p jpeg Experimental Add Sonix 0x0c45:0x607c WC311P jpeg Vga sensor Hv7131R Add Logitech QC chat 0x046d:0x092c thanks Karl Jarrod Hyder for feedback and testing:) FIX Endian problem for ppc and a spca561a chips FIX from Ulisses De Penna Kernel problem with 2.4.23 :) Release 0.56.01 Changes in this release: 19/04/2005: Add Optimedia UsbId 0x06be:0x0800 spca500a Add Toptro Industrial UsbId 0x2899:0x012c spca500a Add Agfa ephoto CL20 UsbId 0x06bd:0x0404 spca500a 15/04/2005: Change version with three part major.minor.edit need for Debian packet all byte change in the code should increment edit by one :) Fix Bug in packet size for buggy chips or Suse kernel Many thanks Antoine Callemeyn for all test and helping:) 14/04/2005: Add Generic 0x04fc:0x7333 as PalmPix DC85 thanks Erick Izquierdo de la Cruz for test snoop :) 09/04/2005: Merge of the Et61xx51 driver in spca5xx works for the Et61x151 and Et61x251 and TAS5130c. 08/04/2005: Seem Benq 3410 is a spca533 with Firmware [spca50x_GetFirmware:446] FirmWare : 24 10 0 5 4 Thanks Ricardo Sanz for test :) 07/04/2005: module can compile now for the old 2.4.x series tested on 2.4.10 as a lot of embedded system use old series:) restore the le16_to_cpu() for kernel up to 2.6.11 usb_control_msg set TimeOut in ms feature kernel 2.6.12 05/04/2005: FIX a bug in EXPERIMENTAL for the spca501 thanks Eric Lunchpail for the patch :) If people need more with the spca501 or the spca508 i did not own webcams with these chips and cannot buy all webcams supported by spca5xx so MAKE a DONATION !! 30/03/2005: only for the spca561a rev 072a: As the Sun in Las Vegas is a strong one :) improve the autobrightness to deal with time-exposure and sensor gain Thanks Chris Cothrun and http://clonepc.biz for testing :) testing FIX a bug the auto brightness now work in read mode instead mmap the small led now go off on close :) 28/03/2005: Set an autobrightness feature for the spca561chips rev 0x072a did not work with the rev 0x012a found in the creative Vista 0x403b for others sorry i did not own those webcams . Add Typhon Webshoot II 0x10fd:0x0128 zc030x Tas5130c thanks Alex Keller for the feedback 27/03/2005: Add Labtec Webcam Elch 2 spca561 Usbid 0x046d:0x0929 thanks Jason Griffiths for feedback and testing :) 19/03/2005: revert the le16_to_cpu() as Tomas (tgc) Ken Huang report code broken with ppc and Arm processor ? 15/03/2005: version:56.03.15: Add MicroInnovation 0x0461:0x0a00 zc030x chips Add Aiptek PocketCam 4 M 0x08ca:0x2028 spca533 Add Aiptek Pocket DV5100 0x08ca:0x2042 spca536 Add Aiptek Pocket DV5300 0x08ca:0x2060 spca536 Add Sunplus Generic 0x04fc:0x5360 spca536 Add Logitech Quick Cam IM +sound 0x046d:0x08a1 Add Logitech QuickCam Chat 0x046d:0x08a3 Add Logitech QuicCam IM 0x046d:0x08B9 13/03/2005: Add Logitech QC IM 0x046d:0x08a0 hdcs2020 thanks Gaby_gaim :) 02/03/2005 version:56.03.02: FIX mirror x axis for the HV7131B :) endian FIX le16_to_cpu() a kernel people feature:) 01/03/2005: version:56.03.01: FIX bug in saturation yuv420p with the LG-Lic100 zc0301P camera Thanks Ulisses Penna with your camera that is more easy :) Add Polaroid PDC3070 Usbid 0x0546:0x3155 spca533a Thanks Mickael for the Patch :) Memory management change 2.6.11 kernel Thanks Brice Goglin and Laurent Riffard for the patch :) Add for test Logitech Quickcam Express Etch2 0x046d:0x0928 Thanks Rick Sewill for testing :) Add for test Logitech Quickam Notebooks 0x046d:0x092a FIX spca5xx_get_depth() according V4L spec driver should answer -EINVAL if a palette is not available only jpeg camera can access the jpeg palette now ALLOW user to change picture parameters trought sysfs as root echo "1" > /sys/module/spca50x/gamma change the gamma value to 1 (for others parameters please read the README) read a parameter cat /sys/module/spca50x/gamma you need to stop the camera then restart to take effect :) improve tasklet ask Change the module name to spca5xx to avoid confusion with the now historical spca50x 06/02/2005: version:56.02.06 Set up version to release.mounth.day Set up tv8532_preprocess to remove miss align pixels sequence move_data did not cut any thing and transmit the whole frame to the decoder with line EOL and packet header Set up Brightness for tv8532 chips Allow yuv420p to use gamma and pictures setting in bayer decode. Good results are get with modprobe spca50x GRed=217 GBlue=300 GGreen=224 gamma=4 Remove Historic BUG in spca50x :)confuse about && and & Thanks Alexander :) FIX bug in Icm105a when restart in large Thanks Chris Cothrun with your Icm105a webcamera that is more easy:) 29/01/2005: Remove big array for the tv8532 and write some usefuff functions instead Fix spca504a when firmware is not get, probe return now -ENODEV :) 26/01/2005: version:5626 Modify spca50x core to accept iso stream for the tv8532 Allow tv8532 to accept 176x144 in hardware and 320x240 software 19/01/2005: Add Typhon Webshoot II 0x10fd:0x8050 Thanks Salvador for the patch :) 16/01/2005: version 0.5616 Creative Vista now start each times :) 352x288 9fp/s 320x240 9fp/s 176x144 16fp/s 160x120 25 fp/s spca561 code give now better results Genius VideoCam V2 352x288 9fp/s 320x240 9fp/s 176x144 17fp/s 160x120 27fp/s For some strange reason Vista contrast did not work ATM :( Know Vista problem a little bit too dark :( 15/01/2005: Big changes in the structure of the module needed by a strong migration all cameras code should goes temporary in a .h file and offer a single interface. Add Creative Vista 0x041e:0x403b experimental work in 352x288 9fp/s:) Know problem very very dark, sometimes need a replug to restart :( 13/01/2005 Find a new hardware setting for the HDCS2020, Many thanks Ulisses Penna for snoop and testing :) 11/01/2005 FIX Bug in the jpeg422 kernel decoder :) 10/01/2005: FIX a bug in the Cx11646 stop isoc :) 09/01/2005: version 0.569 Add colors setting for the cx11646 Clean the isoc handler ( lot of stuff from spca50x without any use ) Preliminary code Creative Vista 0x403b (ATM did not work:) ) Added support for 2 Veo Stingray: 0x0545:0x8333 and 0x0545:0x808b, the latter one has not been confirmed, and is only there for testing purpose!(Tomas works) 07/01/2005: Add contrast brightness for the Cx11646 chips Fix bug when frame asked and get are different Add Sensor CS2102 for the zc030x Thanks Alvaro Salmador for a lot of patchs and test :) Improve Initialize of the tv8532 chips 01/01/2005: Happy New Year :) Add Creative Live! zc0301 chips UsbId 0x041e:0x4036 Thanks Hans Petter Janson :) Add Mercury Digital Pro 3.1Mp UsbId 0x0733:0x2221 Thanks Scott Cameron :) Add Mustek Wcam300a zc0302 UsbId 0x055f:0xc005 Test are needed 31/12/2004 Add Gsmart 300 UsbId 0x055f:0xc200 Thanks Stas Bevc for the pacth and testing :) 25/12/2004: Add the work of Tomas Groth for the tv8532 chips Thanks tgc for the patch :) Changelog of the Tomas branch follow: */25122004: */Re-sync with spca5xx to fix support for > 2.6.5 kernels. */19122004: */Sync with spca5xx to make it compile with gcc 3.4.3 and to make it work with */linux 2.6.10. */Added patch by Sam Engstrom for better picture quality. */23062004: */It turns out that spca532==icm532, and there actually exist a */public availble datasheet for that chip :D */Also there exist a sourceforge project for this chips */(icm532.sf.net), which supports a lot of cams, which i can gladly */here: Biolux 654 microscope, Ezonics EZCam USB II (uvt8532), */Ezonics EZCam USB III, TerraCam USB, Stick Webcam, Mini WebCam, */Tucan PenCam, Che-ez! Webbie, SNAKE EYE SI-8480/8481, PC CAM CP03, */WEB Camera PBC0006, Clipcam. */I hope that i can help the icm532 team and the other way around too. */20062004: */Re-sync with spca5xx experimental driver from mxhaard.free.fr so */that it will work with linux 2.6.7 and above. */030622004: */Re-sync with spca5xx experimental driver from mxhaard.free.fr */Fixed a bug which makes gnomemeeting think the webcam can do */different resolutions, wich it ATM can't. */25052004: */Re-sync with spca5xx experimental driver from mxhaard.free.fr */Made the colors better by switching the bayer-color setup. Release 0.55 Changes in this release: ------------ 24/12/2004: Add bridge cx11646 should be the Creative Notebook PD1170 chips ? Thanks Paolo Gai for sending me a Camera to test :) Add support for the bridge and sensor cxlib.h cx11646.h Add restart marker handle in the jpeg4.2.2 decoder for the cx11646 camera Add JPGC type for that stream FIX problem with old kernel 2.6.4 thanks Tomas (tgc) Add Sunplus 0x08ca:0x2022 Spca533a Aiptek Slim3200 Thanks Daniel Gollub :) Release 0.54 ------------ Changes in this release: 12/12/2004: Add module_param() feature Thanks Reza (timebomb) and me (why not) :) plp can now set some parameters as root trought sysfs echo "2" > /sysfs/module/spca50x/debug then read cat /sysfs/module/spca50x/debug you need to stop the camera then restart to take effect :) FIX an Historical spca50x Bug Thanks Alvaro for the patch :) 10/12/2004: FIX stack problem with Fedora Kernel with a little stack Remove some field not used in the struct frame Remove static variable not used FIX V4l read method only set the real amount of available data Make the code reentrant when needed Lot of cleanup Release 0.53 ------------ Changes in this release: 08/12/2004: Add sensor hv7131c for the z-star chips thanks Cristophe Zaragoza for testing :) 07/12/2004: FIX bug on the ClickSmart310 Gnomemeeting should work now:) 06/12/2004: Implement generic Z-star Vimicro 0x0ac8:0x0302 from Chris Cothrun snoop should be an ICM105A Thanks Chris for testing :) 04/12/2004: Implemente Z-star~vimicro Sensor detection Tested with PB0330 ICM105A HDCS2020 HV7131B TAS5130C HV7131C is untested.For Others contact the Autor mxhaard@free.fr 01/12/2004: Add new spca536 camera Kowa Bs-888e Usbid 0x055f:0xc211 Thanks (nx5) Allow 640x480 jpeg for the spca536 chips 30/11/2004 Sonix decompressor by B.S. is implemented in spcadecoder many Thanks for the great job of B.S. Sonix cam now stream in compressed mode camera type is set to SN9C Update Sonix setting according new frame rate VGA 8~10fp/s CIF 15~18 fp/s QCIF 25~30 fp/s tested with Tas5130 and Pas106 Sensors Fix problem with wMaxPacketSize on zc030x chips wMaxPacketSize is get now from the usb struct enumeration Find some hardware problem with Via VT82C586 Usb controller chips and the Z-Star Vimicro WebCam :( Used an PCI/USB Card with a NEC chips solve the problem :( 21/11/2004 Fix problem on zc030x bridge close (Labtec Pro should stop the light now ) Fix unlink urb or kill urb on close Add contrast and brightness for the zc030x chips Add small udelay between initialize command 18/11/2004 Add for test Creative Nx Pro 2 usbid 0x041e:0x403a Thanks Leonardo Lanzi for testing:) 16/11/2004 Add Digitrex 2110 usbid 0x04fc:0x5330 Thanks Eric Sanden for testing Add Gsmart D30 usbid 0x055f:0xc540 spca533 weird resolution thanks Andrea Musuruane Add for test only Wasam 0x0ac8:0x301b Thanks Gerard Klaver for snoop and test Release 0.52 ------------ 07/11/2004 Add Sonix webcam 0x0c45:0x6029 this is a work from Stefano Mozzi Add Sonix 0x0c45:6009 and 0x0c45:600d Pas106 Sensor Make brightness and contrast working for the Sonix Camera 06/11/2004 use spca50x->present in case of camera disconnect change in usb disconnect to wait on close for release the ressource in case camera is opened FIXME historical reason make the release of ressources hugly call back timer .. that need to rewrite that stuff, disconnect should works but spca50x remain busy :( Remove spin_lock seem not need to protect usb_kill_urb() 30/10/2004 Add Creative Nx Pro zc0301 bridge hv7131b sensor UsbId: 0x041e:0x401e Add Quantization table for the zc030x bridge Allow init_jpeg_decoder to initialize thz good table Some Clean on Labtec Pro Camera code maybe more stable :) 27/10/2004 FIX inline code in spcadecoder.c gcc3.4.2 should be happy now changed by Tomas Groth Thanks :) 22/10/2004 Add usb_kill_urb() for the 2.6.9 kernel Get best performance for the labtec Pro in large. FIX a bug on open isoc urb come before spca50x->user is set for some camera. 14/10/2004 Add New !! Genius VideoCam Express V2 0x0458:0x700f Zc0301P bridge Tas5130c Sensor Thanks Flavio Pescuma for patch and testing :) 12/10/2004 Creative Mobile works now !! 08/10/2004 Add support for the Labtec Pro 0x046d:0x08a2 Thanks "Ellisys" for the usb tracker 110 :) Fix Makefile when the install dir didn't exist. Thanks Marko Djukic :) change version number for the spca5xx spca5xx-08102004 become spca5xx-20041008 01/10/2004 Add support for my Creative Webcam Notebook PD1171 0x041e:0x401f Preleminary support for Creative Mobile PD1090 0x041e:0x4017 (thanks Laurent Cheylus for snoop and testing) Add Bridge ZC3XX for those zc301 zc302 chips Add support for stream JPGH (jpeg 4.2.2) in spcadecoder camera JPEG 4.1.1 and 4.2.2 can use VIDEO_PALETTE_JPEG now Add alternate resolution CIF and QCIF for those VGA SIF chips Release 0.51 ------------ 10/09/2004 FIX a bug in gsmart mini2 maybe 3 initialize (one line whas missed during 2.6.x port)(mxhaard) Add preliminary support Creative Vista 0x041e:0x4018 thanks Nuno Tavares for the patch (Nuno Tavares && mxhaard) Philips K007 spca504a FW 2 1 1 5 2 is supported as the Terratec thanks Marcel Van Nies for the feedback Add Polaroid PDC2030 spca504b 0x0546:0x3273 thanks Stilgar for the feedback 30/08/2004 (mxhaard) Supp Dolphin camera ( Same as AiptekPocketCam 3m 0x2010) Add new palette VIDEO_PALETTE_JPEG application can ask true jpeg now . VIDEO_PALETTE_RAW_JPEG is only here for compatibility but need to be rename as VIDEO_PALETTE_RAW_DATA 28/08/2004 (Tomasz Zablocki && mxhaard ) Add support for the Creative NX ultra This is the work of Tomasz Zablocki (patch test ..) Thanks Tomasz :) Thanks to Semi Malinem for very good snoops with the NX ultra:) Usbid 0x041e:0x401d should be the same with 0x041e:0x4021 ? Merge all command with the spca505 code need to be tested Add enigma dream Epsilon1.3 spca533 Add Dolphin PowerCam2M 0x08ca:0x2011 spca533 Thanks Simon Naunton for the patch :) Add AiptekPocketCam2M 0x08ca:0x2016 spca504b Thanks Jali .. for the patch :) Add 3DemonUsbGrabber 0x0734:0x043b spca506+SAA7113 aka PV321c Thanks Ricardo Ribalda Delgato for the patch :) 23/08/2004(mxhaard) Add contrast and brightness feature for the spca561 no need of force_rgb for the spca561 and sonix chips change setting for sonix contrast 17/08/2004(mxhaard) Sonix 0x0c45:0x6025 works in 640x480 now. I get with spcaview: 2fp/s 640x480 ; 10 fp/s 320x240 ; 15 fp/s 160x120 If someone can help i need the Tasc sensor documentation tas5130d1b. All the result for the sonix chips are get with errors and try again :) Add contrast setting 11/08/2004(mxhaard) FIX the bug in the Iso frame detector !! ( a missing case :( ) 10/08/2004(mxhaard) Damned a bug in the Iso machine with framestart with spca561 spca501 that should be corrected 10/07/2004(mxhaard) Add support for my sonix sn9c102 Usbid 0x0c45:0x6025 Xcam Shanga tested under Kernel 2.4.26 and 2.6.7 Work at 320x240 and 160x120 with brightness command. ATM 640x480 did not work 04/07/2004 (mxhaard) Add DigitalDream Enigma 1.3 Usbid: 0x05da:0x1018 spca504b Thanks Dave Truman patch and testing :) 03/07/2004 Add ClickSmart820 Usbid 0x046d:0x0905 spca533 FW 2 00 00 05 04 (Experimental) Thanks Paul Odin for testing and snoop :) Change max resolution for the MegapixV' and clicksmart820 16/06/2004 Change alternatesetting regime to cur_altsetting as need with kernel 2.6.7 Add macro to use the new delay helper safe instead of wait_ms() 02/06/2004 Preliminary work on spca536 chips Aiptek DV3500 0x08ca 0x2024 Mustek DV4000 0x055f 0xc360 24/05/2004 Add Terratec 2 move 1.3 Usbid 0x04fc:0x504a that camera is an spca504b :) so add a firmware test to choice the good one. 20/05/2004 FIX problem with 2.6.x kernel as usb_set_interface break the usb_submit_urb for the Clicksmart310 spca500 camera.That way the camera should start in any case. Add an info for all the spca500 chip so we can get what sensor is available. 17/05/2004 FIX Oops with spca500_synch310() on open.Seem the alternate setting change need for the clicksmart 310 cause the problem when init_source() initialize the cam 16/05/2004 Add a Bridge list VIDIOCGCAP return in field name the camera name know in Clist[] VIDIOCGCHAN return in field name the bridge know in bridge list These change allow userspace apps to probe the spca5xx camera. spca500 add a request to find what chip sensor is available the i2c chip address should appears in the syslog output Add Clicksmart310 UsbId 0x046d:0x0900 spca500 and HDCS1020 from Agilent . this camera work in 352x288 and sometimes 176x144 If the chips did not start close or Kill video grabber apps replug the cam then restart that should work. As i can see with my Ellysis usb analyser windoze driver on probe start a process to ping the cam every 300ms . 20/04/2004 FIX bug in wakeup that corrupt frame data on smp box Sysfs update the stream_id and add picture parameters 19/04/24 Add Mustek Gsmart Mini Usbid 0x055f:0xc220 Spca500c thanks Ricardo Ferreira For snoop and testing Add Jenoptik JDC21LCD Usbid 0x0733:0x2211 Spca533a thanks Ronan Waide for the patch and testing 16/04/2004 Add a struct pictparam to the decoder. all picture setting is carried to the decoder that way Initial data are set by module parameters (gamma, force_rgb, OffsetRed, OffsetBlue, OffsetGreen, Gain Red, Gain Blue, and Gain Green) Those parameters will change soon trought sysfs feature :) Spcatools will be helping to choose the good setting ATM rgb 16 rgb 24 and rgb 32 are implemented for all cameras Add Instand VCD grabber spca506 and SAA7113 Usbid 0x06e1:0xa190 14/04/2004 Rewrite spca506 SAA7113 code for the usb grabber, WORKS!! fine now :) Allow size to change between 160x120 to 640x480 640x480 need to be interpolate from 640x240 as the windoze driver do :) Allow norme and channel in the VIDIOCSCHAN ioctl norme should be : VIDEO_MODE_PAL, VIDEO_MODE_SECAM, VIDEO_MODE_NTSC NTSC need to be tested i don't own any camera with that feature Channel should be [0..3] for CBVS composite input [6..9] for S-VIDEO input 08/04/2004 Add autobrightness for the spca561 as suggest Saltiel Kenny Thanks :) Change Makefile according autobrightness define 07/04/2004 Add Flycam usb100 Usbid 0x10fd:0x7e50 26/03/2004 FIX bug on wrong pipe handle in 2.4.x -ENXIO message we use urb->next so remove the usb_submit_usb() in the interrupt handler Clean up Makefile 24/03/2004 Add Logitech QuickCam Traveler USB id 0x046d 0x0890 Change in spca50x_probe() no need to test vendorClass and subClass Test probe only on Interface 0 21/03/2004 Change makefile according advise by Luca Risolia for modules versions set to Yes 14/03/2004 FIX bug in Control Buffer allocation need GFP_KERNEL init_jpeg_decoder is now on open if necessary Add Creative PCCam750 Usb id 0x041e 0x4013 Add Maxell PM3 Usb id 0x060b 0xa001 Add Benq DC1016 Usb id 0x04a5 0x300c Add Benq DC3410 Usb id 0x04a5 0x300a Add Micro Innovation Usb id 0x0461 0x0815 Change spcaCameraStart(); spcaCameraStop(); 12/03/2004 FIX compilation problem under kernel 2.4.24 01/03/2004 FIX a small bug in bayer decode YUV or YVU tha's the question 28/02/2004 change down(&spca50x->buf_lock) to down_trylock(&spca50x->buf_lock); Thanks Brian Perkins for the advise FIX bug in bayer decoder Thanks Jakub Krajcovic for the spca561 raw data from spcaview -v FIX bug in crop feature with no jpeg camera Thanks Peter Cook for the spca501 raw data from spcaview -v FIX force_rgb for VIDEO_PALETTE_RGB565 Add Mustek DV3000 spca533 UsbId 0x055f:0xc440 Thanks Arne Georg Gledish Add HamaSightcam 2 spca508 UsbId 0x0af9:0x0011 Thanks Tomas Groth (Tgc) Add Benq DC1300 spca504b UsbId 0x04a5:0x3003 Thanks Mick Hellstrom 22/02/2004 Merge 2.4.x and 2.6.x change 08/02/2004 Port the change for 2.6.x Kernel thanks Reza Jelveh Split the code in spcaCompat for v4l layer rewrite the Makefile according all changes 28/01/2004 ClickSmart 510 have 3 interfaces FIX the probe function to support that feature FIX the autodetect of cam, UsbId 0x0000 0x0000 is set at the end of the list 24/01/2004 Add ClickSmart 510 Experimental Untested Usbid 0x046d:0x0901 Allow Spca505 to work under Gnomemeeting Add Spca505 resolution 320x240 07/01/2003 Add Benq DC1500 spca533 need test from Isabel (spain)Usbid 0x04a5:0x3008 Add Logitech Cliksmart 420 spca504 FW near PCCAM 600 Usbid 0x046d:0x0960 Thanks Bummerlord for the Patch 28/12/2003 Add yuv420p to the bayer_decode() Also block the drop frame feature for spca501 testing in 640x480 27/12/2003 Add bayer_decode() for GBRG spca561 camera RGB16 RGB24 RGB 32 is allowed 26/12/2003 Change spca50x_smallest_index to avoid pipe size according width and height Add on open default palette to RGB24 allow gqcam to work fine in all case :) Add spca50x->minwidth and spca50x->minheight Configure sensor take care of min too 25/12/2003 Add yuyv yyuv to yuv_decode Add RGGB Bayer type for the spca501 cam in native stream format Prototype bayer_decode() for those cams Add spca5xx_gamma.h allow 6 gamma tables to correct cams stream A static int gCor select the Table for all the cams in rgb mode value 0 to 6 is allowed 0->2.2, 1->1.7, 2->1.45, 3->1, 4->0.6896, 5->0.5882, 6->0.4545 Big Cut in the interrupt Handler all cam process the Tasklet:) Change read function according that change Configure sensor now take care of the maxheight and maxwidth of each cam send to Tomas Groth (tgc) for spca508 testing. send to Linus McCabe (McCabe) for spca501 testing. 16/12/2003 Fix the rgb for spca508 thanks Tomas (tgc) for the LastTestOfTheNight :) 15/12/2003 yuv420p ok with spca508 need more test on rgb 14/12/2003 Add yuvy_decode() for video_palette rgb 16 24 32 and yuv420p with cropping feature for the spca508 that need tgc test 12/12/2003 Add enum type for native cam stream jpeg yuvy yyuv yuyv grey Add field cameratype in spca50x_frame to get the good outpout method for that cam Add Pure Digital Dakota UsbId 0x04fc:0xffff camera Thanks Thomas Steffen for the patch 30/11/2003 Add the same init code for the Intel Cam CS630 spca500a Thanks Rob Roschewsk for testing 29/11/2003 The spca500 from FamilyCam and pocket DV also i think clicksmart510 need to initialise with the size set to 640x480 and the corresponding usb pipe set to 1023.Unfortunately our driver start with the smallest one.I have made a change to allow these cams to work with all size 640x480 320x240 and 176x144 the large size auto init without unplug and replug the cam the others need that:( Need more test for the pocketDV owner. 23/11/2003 Tempory FIX for Trust familycam@300 work on 640x480 and 320x240 need to remove the cable between every usage.The frame rate is function of light exposure between 4fps for dark to 14fps in daylight.Thanks for Denis Pitzalis who give me a cam for testing 16/11/2003 Remove the vfree a line 1094 seem to be a bug in case of camera disconnect.Dealloc do the job too ? FIX bug with the Suse Kernel on close function 14/11/2003 schedule a outpict tasklet in the interrupt handler we work fine now at all resolutions all palettes all sizes:) For a (small computer < Pentium or athlon) please change the active Makefile to Makefile.i386 Thanks Stephane and Marek for Testing. 11/11/2003 All the crop feature are now implemented and work fine for the spca504a and spca504b camera the spca533a work fine in 320x240 194x144 176x144 the others want the multiply and is not implemented ATM I hope this code have not break the others cam.If you got problem or working please feedback to mxhaard@magic.fr The hint message have move to "SPCA5XX Usb Camera" to allow all the palette working on gnomemeeting. Thanks to Damien Sandras and Fabrice (Gnomemeeting Team) for help and testing the YUV420P feature. A preliminary code to drop frame is also implemented this drop occurs when the userspace ask for RGB in full size 640x480 like "gqcam". (That is not really a good idea, we want 18.43Mb/s to push a whole frame in RGB24 and the kernel didn't like that:) ) a better idea is to use yuv420p with spcaview that need only 9.21 Mb/s with the same quality. IMHO the decoder to RGB feature didn't have a place in a Linux kernel:) This module need a good processor to work fine and is optimize for i686 familly ( PIV and Athlon) 07/11/2003 Start to implement the output feature for sif qsif cif qcif vga qpal Encode the struct spca5xx_ext_mode to allow a hardware width and heigth and a software methode the lower nibble give the code for hardware the height give the method 0-> nothing 1-> cropping 2-> divide 4-> multiply value1 give a X value and value2 give the Y. cropping method each X or Y value is used for a macroblock 16x16 pixels if X is not odd then the first crop begin at the left is more shorter (1) than the other each crop is perform in center method. divide divide is a crop pixel on the surface and is perform in X width and Y height multiply add a new pixel every X in the width axis (need for the spca533a) 02/11/2003 FIX a bug in spca50x_read function producer to comsumer if the comsumer ask n bytes the producer give n bytes. That fix the read function with spcaview in yuv420p and rawjpeg data 29/10/2003 Add VIDEO_PALETTE_RGB565 for the rawjpeg camera 21/10/2003 Expand the macro of IDCT to remove a lot of exchange perform IMULT with long Start to implement crop feature for rawjpeg cam this will need for all the resolutions 20/10/2003 Test the idct and profile to get best performance now work best with full size in real times 16fps in my Athlon 2.2Ghz 15/10/2003 rewrite the rgb decoder for 24 and 32 bits; you can change the color space in the code uncomment the relevant line in jpegdecoder.c initcol() is not need because break the yuv colors space. 14/10/2003 some cleanup rewrite the main loop of jpegdecode seem the huffman/idct in large with a good light take about 3ms 13/10/2003 rearrange the jpegdecode function look at the UV plan and remove the rescale when YUVP is asked ( work with large resolution in low light, need optimisations) 11/10/2003 Implement YUV420P for the spca500 spca504a spca504b spca533a in libjpeg decoder unlock VIDEO_PALETTE_YUV420P in spca50x for those chips (2628) 01/10/2003 Add Code from snoop for the mysterious cam 0x0000 0x0000 27/09/2003 Make the code in accord with CVS TREE Try the mysterious cam from Ori Usbid 0x0000 0x0000 Code seem like the Arowana 14/09/2003 Add Code for the Aiptek PocketDV UsbId 0x08ca:0x0103 For testing Marek Blasko 31/08/2003 Add Code for Megapix V4 for testing Snoopy file and test from J�g B�nke 30/08/2003 Add Mustek mdc5500z Thanks Piotr Pawlow for patch 23/08/2003 Fix Aiptek 1.3 drop packets Works 640x480 and 320x240 Thanks Fransico Ramiro Pereira,Scott Barnes and Gregor Hoffleit. Make own procedure for Aiptek1.3 12/08/2003 Add Code for Mustek Gsmart LCD3 Snoopy file and test from Piotr Pawlow Thanks Works 320x240 and 464x352 Thanks Piotr 6/08/2003 Seem the code found on Macam project work for the gsmart mini not for Aiptek Fix Aiptek 1.3 command loop 4/08/2003 Add: Aiptek PenCam SD 2 0x08ca 0x2018 thanks Wolfang Samsinger. Add: Aiptek mini PenCam 1.3 Mega Help with the code found on Macam project (Mac OSX) * Temporary fixes for the Intel Pocket PC Camera and the DSC-350. Both cameras are spca500a based, and require a qtable to be defined. The temporary fix makes these two cameras use the Creative PC-CAM 300 qtable, but ideally, they would use the same qtable as the windows driver (polesworth). * Support for spca504 based cams with a firmware version 2 (spca504b) (mxhaard). * Conditionally follow memory management API changes backported to 2.4.20 in redhat 9 (Chris Parker ). * Imported Creative PC-CAM 600 patches from by , and tidied them up (polesworth). * Ratified spca504 code to use the same Q-table support code as spca500, and moved all Q-tables into their own header file (polesworth). * Fix for WOLT kernel. pte_offset changed to pte_offset_kernel (polesworth). * Fix for a link error. The option '--relocateable' doesn't exist in all versions of ld. Instead, use '-r' which should work on all platforms (pancake ). * Support for the Hama USB Sightcam 100 (tgc). * Added missing entries for Aiptek MegaCam (polesworth). * Support for the Arowana 300k CMOS Camera (Daniele Gozzi - codeatnight). * Makefile cleanups (Anders Kaseorg - anders1). * Added support for (mxhaard): - Aiptek PocketDVII 1.3; - Aiptek Pencam SD 2M; - Mustek MDC5500Z; - Megapix V4; - Aiptek PocketDV; - Mystery Camera (from Ori). * Cleaned up internal camera id allocation, including removal of unnecessary checks against USB vendor/device id (polesworth). * Various tidying up - mainly white space changes (polesworth). * Merged in patch #802598 (SPCA561 alpha support), which provides support for the Genius VideoCAM Express V2 (spca561a) (enjolras). * Reformatting changes, mainly in spca50x.h (polesworth). Release 0.30 ------------ This release coordinated by Miah Gregory (polesworth) and Till Adam (tillsan). Binary and source packages available for this release. Changes in this release: * Fixes for compilation against 2.4.20 kernels (polesworth). * Couple of fixups for gcc 3.2 pedantics (polesworth). * Fixed destroy proc debug code, which used an uninitialised variable 'name'. Now the variable is displayed after it has been initialised (Andrea Mennucc). * Added support for spca504 based cameras (tillsan, feber). * Added support for spca500 based cameras (polesworth). * As part of the spca500/spca504 support, added jpeg decompression code (feber, polesworth, tillsan). * Increased MAX_FRAME_SIZE to allow 32 bit V4L modes (tillsan). * Changed default debug level to 0 (polesworth). * Added raw proc entry support - useful for debugging (polesworth). * Added support for: - Mustek gSmart mini 2; - Mustek gSmart mini 3; - Creative PC-CAM 300; - D-Link DSC-350; - Creative PC-CAM 600; - Intel Pocket PC Camera. * Fix deadlock when shutting down the camera while used by a program (feber). * Merged in patch #505324 (Patch for the Intel PC Camera CS110) (polesworth). * Merged in patch #589680 (Prelim 3Com HomeConnect Lite Support) (polesworth). * Fixed spca50x_probe, which was handling some cameras with known vendor ids but unknown device ids incorrectly. Found this bug whilst looking into support for the Kodak EZ200 (polesworth). * Fixed driver to select smallest available resolution for a particular bridge, rather than using a hardcoded value (polesworth). * Tidied up the jpeg decoder, which turned out to be including userspace header files, and added a return value to jpeg_decode (polesworth). * spca500: changing mode now works correctly (polesworth). * spca500: enabled subsampling for modes smaller than 640x480 (polesworth). * spca500: added rudimentary picture quality controls (polesworth). * Added support for the Kodak EZ200 camera, which is spca500 based (polesworth). * Added in some changes for spca505 and maybe spca506 which apparently make the brightness levels usable (Keith MacLeod / Eric Preston). Release 0.20 ------------ This release coordinated by Miah Gregory (polesworth). For this release, it was decided to package up source only, and not binaries. This will change in the future, once the driver stabilises. Changes in this release: * Fix bug with probe function handling in kernels > 2.3.x (Leonardo Milano). * Removed XawTV hack (use XawTV >= 3.65) (Leonardo Milano/korovkin). * Added SPCA508 support for the ViewQuest VQ110 (David Adler). * Spca50x_read infinite loop protection check (David Adler). * Palette mode check (David Adler). * VQ M318B description (David Adler). * Added Kodak DVC-325 to supported cam list (Sebastian Voitzsch). * Moved initialisation code to seperate header files (korovkin). * Added some experimental code for brightness autoadjustment (korovkin). * Change from tq_scheduler to schedule_task (dscottie). * Quick fix for Gnomemeeting/PWLib problems seen on SPCA50X; VIDIOCSWIN sets size of frame[0] to new size (dscottie). * Forced driver to say which resolutions it supports, in order to force gnomemeeting to emulate the mode, if it's not supported. * Added control proc entry, which allows the user to change driver parameters without reinserting the module (korovkin). * Added "whiteness" autoadjustment feature (korovkin). * Fixed bug in VIDIOCSPICT ioctl handling in SPCA501 (korovkin). * Fixed bridge type check in spca50x_close (korovkin). * Fixed memory leak bug in spca50x_alloc (Amit Bhalla/korovkin). * Added average R-6/B-6 in prox/.../spca50x/videoX entry (korovkin). * Added ability to adjust red/blue offsets via Hue and Color settings (korovkin). * Added GPL Licence information, for 2.4 series kernels (polesworth). * Rejigged the Makefile and #defines (polesworth). * Fixed bug in procfs support, whereby control file was created multiple times, but with the same name. We now create /proc/video/spca50x/controlX. (polesworth/korovkin). * Various other minor changes/fixes. Release 0.10 ------------ This release coordinated by Darrell Scott (dscottie). Changes in this release: * Added support for ViewQuest M318B (0733:0402) (Leonardo Milano) * Improved support for XawTV (korovkin) * Improved Intel Create and Share (SPCA501-based cams) support (korovkin) * Fixed initialization bug for connection/disconnection support (korovkin) Release 0.9 ----------- This release coordinated by Joel Crisp (cydergoth). Changes in this release: * Fixed compile with CONFIG_FB and no video/font.h * Changed decode of 501 data - may now work. * Updated some documentation Previous releases not documented here. pwcbsd/spca5xx-20060402/INSTALL000700 000423 000000 00000002717 10546676140 016126 0ustar00luigiwheel000000 000000 Module compile outside the kernel tree but need the source of your running kernel installed and configured. be sure your kernel include usb and v4l stuff Kernel 2.4.x configure your kernel make dep go to the spca5xx directories make clean (to be sure) make if all goes right as root : make install Kernel 2.6.x make clean make if all goes right as root : make install To test please use the command line spcaview tool. You need libsdl installed with the header from your distro or goto http://www.libsdl.org /*******************************************************************************************/ SPCAVIEW is the only V4L1 Apps for testing your SPCA5XX webcam. IF THAT WORK WITH SPCAVIEW ALL OTHERS V4L1 APPS SHOULD WORK !!! DON'T ASK ME ABOUT : XAWTV GNOMEMEETING MOTION EKIGA AMSN ZONEMINDER CAMORAMA CAMSTREAM VIDEODOG PALANTIR SPOKE CAMSOURCE MPLAYER VLC FFMPEG MPEG4IP ..... ASK THE AUTHORS. YOU SHOULD GET A BETTER ANSWER !!! /*******************************************************************************************/ plug the cam as root : lsmod spca5xx should be load with videodev, if not, somethings goes wrong, verify that your cam is supported if not and you are sure for a Sunplus chip goto irc.freenode.net channel #spca50x and ask for help if yes run spcaview with this parameters (maybe read the readme is the best) spcaview -d /dev/video0 -f yuv if you have a bttv card use change the video0 to video1 that should work Enjoy !! pwcbsd/spca5xx-20060402/LICENSE000644 000423 000000 00000043110 10546676140 016101 0ustar00luigiwheel000000 000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. pwcbsd/spca5xx-20060402/Makefile000644 000423 000000 00000016030 10546676140 016535 0ustar00luigiwheel000000 000000 VERSION = 00.57.11 CVSVERSION = "$Experimental work Michel Xhaard && Reza Jelveh 03/02/2004" DEFINES = ### # The following flags enable experimental features. # By default, these are enabled for development versions of the driver, and # disabled for release versions. # Optional: Enable driver debugging DEFINES += -DSPCA50X_ENABLE_DEBUG # Optional: Enable some experimental brightness control on internal CCD # DEFINES += -DSPCA50X_ENABLE_EXP_BRIGHTNESS # Optional: Enable some miscellaneous experimental code # DEFINES += -DSPCA50X_ENABLE_EXPERIMENTAL # Optional: Enable direct register read/write for PAC207 development #DEFINES += -DSPCA5XX_ENABLE_REGISTERPLAY ### # The following flags enable features that aren't yet implemented, and # therefore are disabled by default. # Optional: Enable compression DEFINES += -DSPCA50X_ENABLE_COMPRESSION ### # Rest of Makefile follows here. You probably won't need to touch this. # Setup defines DEFINES += -DCONFIG_USB_SPCA5XX_MODULE=1 -DMODULE -D__KERNEL__ DEFINES += -DVID_HARDWARE_SPCA5XX=0xFF -DSPCA5XX_VERSION=\"$(VERSION)\" ifneq ($(shell uname -r | cut -d. -f1,2), 2.4) ifneq ($(KERNELRELEASE),) # We were called by kbuild CFLAGS += $(DEFINES) obj-m += spca5xx.o spca5xx-objs := drivers/usb/spca5xx.o drivers/usb/spcadecoder.o else # We were called from command line KERNEL_VERSION = `uname -r` KERNELDIR := /lib/modules/$(KERNEL_VERSION)/build PWD := $(shell pwd) MODULE_INSTALLDIR = /lib/modules/$(KERNEL_VERSION)/kernel/drivers/usb/media/ # Targets, don't change! default: @echo ' Building SPCA5XX driver for 2.5/2.6 kernel.' @echo ' Remember: you must have read/write access to your kernel source tree.' $(MAKE) -C $(KERNELDIR) SUBDIRS=$(PWD) CC=$(CC) modules install: mkdir -p $(MODULE_INSTALLDIR) rm -f $(MODULE_INSTALLDIR)spca50x.ko rm -f $(MODULE_INSTALLDIR)et61x.ko install -c -m 0644 spca5xx.ko $(MODULE_INSTALLDIR) /sbin/depmod -ae uninstall: rm -f $(MODULE_INSTALLDIR)/spca5xx.ko /sbin/depmod -aq endif else # kernel version test ############################################################################# # For Linux 2.4 users. # Change the following lines according to your system configuration. # It is important to configure your particular source tree ("make dep") before # compiling this module! ############################################################################# ### # This makefile will build the spca50x driver module external to the kernel # source tree. It makes it easier to swap kernels. KERNEL_VERSION = `uname -r` ### # Location of the header files (most importantly the config files) # for the kernel you want to build the module against. # This should be correct for the currently installed kernel on your machine. KINCLUDE = /lib/modules/$(KERNEL_VERSION)/build/include KERNEL_ACFILE = $(KINCLUDE)/linux/autoconf.h KERNEL_MODVERSIONSFILE = $(KINCLUDE)/linux/modversions.h MODULE_INSTALLDIR = /lib/modules/$(KERNEL_VERSION)/kernel/drivers/usb/ # Detect module versioning support ifneq ($(strip $(shell grep 'define CONFIG_MODVERSIONS 1' $(KERNEL_ACFILE))),) DEFINES += -DMODVERSIONS -include $(KERNEL_MODVERSIONSFILE) endif # Detect SMP support ifneq ($(strip $(shell grep 'define CONFIG_SMP 1' $(KERNEL_ACFILE))),) DEFINES += -D__SMP__ -DSMP endif # Setup the tools CC = gcc LD = ld # Setup compiler warnings WARNINGS = -Wall -Wpointer-arith WARNINGS += -Wcast-align -Wwrite-strings -Wstrict-prototypes WARNINGS += -Wuninitialized -Wreturn-type -Wunused -Wparentheses # Setup compiler flags CFLAGS = -O2 -fomit-frame-pointer -fno-strict-aliasing -pipe CFLAGS += -mpreferred-stack-boundary=2 CFLAGS += -I$(KINCLUDE) -Idrivers/usb # Setup link flags LDFLAGS = --strip-debug -r # Setup the list of files to be included in a distribution DIST_FILES = CHANGELOG \ README \ Makefile \ drivers/usb/Config.in \ drivers/usb/spcadecoder.c \ drivers/usb/spcadecoder.h \ drivers/usb/spcagamma.h \ drivers/usb/spcaCompat.h \ drivers/usb/spcausb.h \ drivers/usb/spca500_init.h \ drivers/usb/spca501_init.h \ drivers/usb/sp5xxfw2.dat \ drivers/usb/sp5xxfw2.h \ drivers/usb/spca505_init.h \ drivers/usb/spca506.h \ drivers/usb/spca508_init.h \ drivers/usb/spca561.h \ drivers/usb/sonix.h \ drivers/usb/cs2102.h \ drivers/usb/hv7131b.h \ drivers/usb/icm105a.h \ drivers/usb/hv7131c.h \ drivers/usb/hdcs2020.h \ drivers/usb/pb0330.h \ drivers/usb/tas5130c.h \ drivers/usb/zc3xx.h\ drivers/usb/tv8532.h\ drivers/usb/cxlib.h\ drivers/usb/sn9cxxx.h\ drivers/usb/cx11646.h\ drivers/usb/pac207.h\ drivers/usb/spca5xx.c \ drivers/usb/spca5xx.h OBJS = drivers/usb/spcadecoder.o \ drivers/usb/spca5xx.o BINARY = spca5xx.o ### # Targets follow here binary: $(OBJS) @echo Linking $(BINARY) @$(LD) $(LDFLAGS) -o $(BINARY) $(OBJS) install: binary @echo Installing.. Your root password may be required. su -c "make install-root" install-root: @echo Installing.. @mkdir -p /lib/modules/`uname -r`/kernel/drivers/usb @rm -f /lib/modules/`uname -r`/kernel/drivers/usb/spca50x.o @rm -f /lib/modules/`uname -r`/kernel/drivers/usb/et61x.o @cp spca5xx.o /lib/modules/`uname -r`/kernel/drivers/usb/spca5xx.o @/sbin/depmod dist: clean binary @echo Making distributable archives @rm -f spca5xx-src-$(VERSION).tar.gz @tar zcf spca5xx-src-$(VERSION).tar.gz $(DIST_FILES) @rm -f spca5xx-module-$(VERSION).tar.gz @cp $(BINARY) spca5xx-$(VERSION).o @tar zcf spca5xx-module-$(VERSION).tar.gz spca5xx-$(VERSION).o README @rm spca5xx-$(VERSION).o .c.o: Makefile $*.c @echo Compiling $*.c @$(CC) $(CFLAGS) $(WARNINGS) $(DEFINES) -c $*.c -o $*.o ### # Dependencies follow here drivers/usb/spca5xx.o: drivers/usb/spca5xx.h \ drivers/usb/spcaCompat.h \ drivers/usb/spcausb.h \ drivers/usb/sonix.h \ drivers/usb/spca500_init.h \ drivers/usb/spca501_init.h \ drivers/usb/sp5xxfw2.h \ drivers/usb/spca505_init.h \ drivers/usb/spca506.h \ drivers/usb/spca508_init.h \ drivers/usb/spca561.h \ drivers/usb/zc3xx.h\ drivers/usb/tv8532.h\ drivers/usb/cx11646.h\ drivers/usb/mr97311.h\ drivers/usb/sn9cxxx.h\ drivers/usb/pac207.h\ drivers/usb/spcadecoder.o: drivers/usb/spcadecoder.h \ drivers/usb/spcagamma.h \ endif # End kernel version test ############################################################################## # OTHER TARGETS ############################################################################## clean: rm -r -f drivers/usb/*.o drivers/usb/.spcadecoder.o.cmd \ drivers/usb/.spca5xx.o.cmd *.o *.ko *.mod.* .[a-z]* core *.i ############################################################################## pwcbsd/spca5xx-20060402/README000700 000423 000000 00000051452 10546676140 015755 0ustar00luigiwheel000000 000000 README What is it? =========== This is version 0.57.xx of the spca5xx video for linux (v4l) driver, providing support for webcams and digital cameras based on the spca5xx range of chips manufactured by SunPlus Sonix Z-star Vimicro Conexant Etoms Transvision Mars-Semi Pixart Please address all correspondence to , or make use of the bug/support/patch tracking facilities provided by SourceForge, at . Disclaimer ========== We believe that this driver will perform correctly in many circumstances. It is, however, experimental code, running at the kernel level, and may potentially cause serious data corruption, or worse. Do not use this driver unless you are prepared for this eventuality. Use of this driver constitutes an agreement that no-one other than yourself may be held responsible for any effects caused by the driver, ill or otherwise. What cameras are supported? =========================== Currently, the following cameras are supported by this driver: Vendor ID Device ID Support Summary --------- --------- --------------- {USB_DEVICE(0x0733, 0x0430)}, /* Intel PC Camera Pro */ {USB_DEVICE(0x0733, 0x0401)}, /* Intel Create and Share */ {USB_DEVICE(0x99FA, 0x8988)}, /* Grandtec V.cap */ {USB_DEVICE(0x0733, 0x0402)}, /* ViewQuest M318B */ {USB_DEVICE(0x0733, 0x0110)}, /* ViewQuest VQ110 */ {USB_DEVICE(0x040A, 0x0002)}, /* Kodak DVC-325 */ {USB_DEVICE(0x055f, 0xc420)}, /* Mustek gSmart Mini 2 */ {USB_DEVICE(0x055f, 0xc520)}, /* Mustek gSmart Mini 3 */ {USB_DEVICE(0x041E, 0x400A)}, /* Creative PC-CAM 300 */ {USB_DEVICE(0x084D, 0x0003)}, /* D-Link DSC-350 */ {USB_DEVICE(0x041E, 0x400B)}, /* Creative PC-CAM 600 */ {USB_DEVICE(0x8086, 0x0630)}, /* Intel Pocket PC Camera */ {USB_DEVICE(0x8086, 0x0110)}, /* Intel Easy PC Camera */ {USB_DEVICE(0x0506, 0x00df)}, /* 3Com HomeConnect Lite */ {USB_DEVICE(0x040a, 0x0300)}, /* Kodak EZ200 */ {USB_DEVICE(0x04fc, 0x504b)}, /* Maxell MaxPocket LE 1.3 */ {USB_DEVICE(0x08ca, 0x2008)}, /* Aiptek Mini PenCam 2 M */ {USB_DEVICE(0x08ca, 0x0104)}, /* Aiptek PocketDVII 1.3 */ {USB_DEVICE(0x08ca, 0x2018)}, /* Aiptek Pencam SD 2M */ {USB_DEVICE(0x04fc, 0x504a)}, /* Aiptek Mini PenCam 1.3 */ {USB_DEVICE(0x055f, 0xc530)}, /* Mustek Gsmart LCD 3 */ {USB_DEVICE(0x055f, 0xc650)}, /* Mustek MDC5500Z */ {USB_DEVICE(0x052b, 0x1513)}, /* Megapix V4 */ {USB_DEVICE(0x08ca, 0x0103)}, /* Aiptek PocketDV */ {USB_DEVICE(0x0af9, 0x0010)}, /* Hama USB Sightcam 100 */ {USB_DEVICE(0x1776, 0x501c)}, /* Arowana 300K CMOS Camera */ {USB_DEVICE(0x08ca, 0x0106)}, /* Aiptek Pocket DV3100+ */ {USB_DEVICE(0x08ca, 0x2010)}, /* Aiptek PocketCam 3M */ {USB_DEVICE(0x0458, 0x7004)}, /* Genius VideoCAM Express V2 */ {USB_DEVICE(0x04fc, 0x0561)}, /* Flexcam 100 */ {USB_DEVICE(0x055f, 0xc430)}, /* Mustek Gsmart LCD 2 */ {USB_DEVICE(0x04fc, 0xffff)}, /* Pure DigitalDakota */ {USB_DEVICE(0xabcd, 0xcdee)}, /* Petcam */ {USB_DEVICE(0x04a5, 0x3008)}, /* Benq DC 1500 */ {USB_DEVICE(0x046d, 0x0960)}, /* Logitech Inc. ClickSmart 420 */ {USB_DEVICE(0x046d, 0x0901)}, /* Logitech Inc. ClickSmart 510 */ {USB_DEVICE(0x04a5, 0x3003)}, /* Benq DC 1300 */ {USB_DEVICE(0x0af9, 0x0011)}, /* Hama USB Sightcam 100 */ {USB_DEVICE(0x055f, 0xc440)}, /* Mustek DV 3000 */ {USB_DEVICE(0x041e, 0x4013)}, /* Creative Pccam750 */ {USB_DEVICE(0x060b, 0xa001)}, /* Maxell Compact Pc PM3 */ {USB_DEVICE(0x04a5, 0x300a)}, /* Benq DC3410 */ {USB_DEVICE(0x04a5, 0x300c)}, /* Benq DC1016 */ {USB_DEVICE(0x0461, 0x0815)}, /* Micro Innovation IC200 */ {USB_DEVICE(0x046d, 0x0890)}, /* Logitech QuickCam traveler */ {USB_DEVICE(0x10fd, 0x7e50)}, /* FlyCam Usb 100 */ {USB_DEVICE(0x06e1, 0xa190)}, /* ADS Instant VCD */ {USB_DEVICE(0x055f, 0xc220)}, /* Gsmart Mini */ {USB_DEVICE(0x0733, 0x2211)}, /* Jenoptik jdc 21 LCD */ {USB_DEVICE(0x046d, 0x0900)}, /* Logitech Inc. ClickSmart 310 */ {USB_DEVICE(0x055f, 0xc360)}, /* Mustek DV4000 Mpeg4 */ {USB_DEVICE(0x08ca, 0x2024)}, /* Aiptek DV3500 Mpeg4 */ {USB_DEVICE(0x046d, 0x0905)}, /* Logitech ClickSmart820 */ {USB_DEVICE(0x05da, 0x1018)}, /* Digital Dream Enigma 1.3 */ {USB_DEVICE(0x0c45, 0x6025)}, /* Xcam Shanga */ {USB_DEVICE(0x0733, 0x1311)}, /* Digital Dream Epsilon 1.3 */ {USB_DEVICE(0x041e, 0x401d)}, /* Creative Webcam NX ULTRA */ {USB_DEVICE(0x08ca, 0x2016)}, /* Aiptek PocketCam 2 Mega */ {USB_DEVICE(0x0734, 0x043b)}, /* 3DeMon USB Capture aka */ {USB_DEVICE(0x041E, 0x4018)}, /* Creative Webcam Vista (PD1100) */ {USB_DEVICE(0x0546, 0x3273)}, /* Polaroid PDC2030 */ {USB_DEVICE(0x041e, 0x401f)}, /* Creative Webcam Notebook PD1171 */ {USB_DEVICE(0x041e, 0x4017)}, /* Creative Webcam Mobile PD1090 */ {USB_DEVICE(0x046d, 0x08a2)}, /* Labtec Webcam Pro */ {USB_DEVICE(0x055f, 0xd003)}, /* Mustek WCam300A */ {USB_DEVICE(0x0458, 0x7007)}, /* Genius VideoCam V2 */ {USB_DEVICE(0x0458, 0x700c)}, /* Genius VideoCam V3 */ {USB_DEVICE(0x0458, 0x700f)}, /* Genius VideoCam Web V2 */ {USB_DEVICE(0x041e, 0x401e)}, /* Creative Nx Pro */ {USB_DEVICE(0x0c45, 0x6029)}, /* spcaCam@150 */ {USB_DEVICE(0x0c45, 0x6009)}, /* spcaCam@120 */ {USB_DEVICE(0x0c45, 0x600d)}, /* spcaCam@120 */ {USB_DEVICE(0x04fc, 0x5330)}, /* Digitrex 2110 */ {USB_DEVICE(0x055f, 0xc540)}, /* Gsmart D30 */ {USB_DEVICE(0x0ac8, 0x301b)}, /* Asam Vimicro */ {USB_DEVICE(0x041e, 0x403a)}, /* Creative Nx Pro 2 */ {USB_DEVICE(0x055f, 0xc211)}, /* Kowa Bs888e Microcamera */ {USB_DEVICE(0x0ac8, 0x0302)}, /* Z-star Vimicro zc0302 */ {USB_DEVICE(0x0572, 0x0041)}, /* Creative Notebook cx11646 */ {USB_DEVICE(0x08ca, 0x2022)}, /* Aiptek Slim 3200 */ {USB_DEVICE(0x046d, 0x0921)}, /* Labtec Webcam */ {USB_DEVICE(0x046d, 0x0920)}, /* QC Express */ {USB_DEVICE(0x0923, 0x010f)}, /* ICM532 cams */ {USB_DEVICE(0x055f, 0xc200)}, /* Mustek Gsmart 300 */ {USB_DEVICE(0x0733, 0x2221)}, /* Mercury Digital Pro 3.1p */ {USB_DEVICE(0x041e, 0x4036)}, /* Creative Live ! */ {USB_DEVICE(0x055f, 0xc005)}, /* Mustek Wcam300A */ {USB_DEVICE(0x041E, 0x403b)}, /* Creative Webcam Vista (VF0010) */ {USB_DEVICE(0x0545, 0x8333)}, /* Veo Stingray */ {USB_DEVICE(0x0545, 0x808b)}, /* Veo Stingray */ {USB_DEVICE(0x10fd, 0x8050)}, /* Typhoon Webshot II USB 300k */ {USB_DEVICE(0x0546, 0x3155)}, /* Polaroid PDC3070 */ {USB_DEVICE(0x046d, 0x0928)}, /* Logitech QC Express Etch2 */ {USB_DEVICE(0x046d, 0x092a)}, /* Logitech QC for Notebook */ {USB_DEVICE(0x046d, 0x08a0)}, /* Logitech QC IM */ {USB_DEVICE(0x0461, 0x0a00)}, /* MicroInnovation WebCam320 */ {USB_DEVICE(0x08ca, 0x2028)}, /* Aiptek PocketCam4M */ {USB_DEVICE(0x08ca, 0x2042)}, /* Aiptek PocketDV5100 */ {USB_DEVICE(0x08ca, 0x2060)}, /* Aiptek PocketDV5300 */ {USB_DEVICE(0x04fc, 0x5360)}, /* Sunplus Generic */ {USB_DEVICE(0x046d, 0x08a1)}, /* Logitech QC IM 0x08A1 +sound */ {USB_DEVICE(0x046d, 0x08a3)}, /* Logitech QC Chat */ {USB_DEVICE(0x046d, 0x08b9)}, /* Logitech QC IM ??? */ {USB_DEVICE(0x046d, 0x0929)}, /* Labtec Webcam Elch2 */ {USB_DEVICE(0x10fd, 0x0128)}, /* Typhoon Webshot II USB 300k 0x0128 */ {USB_DEVICE(0x102c, 0x6151)}, /* Qcam Sangha CIF */ {USB_DEVICE(0x102c, 0x6251)}, /* Qcam xxxxxx VGA */ {USB_DEVICE(0x04fc, 0x7333)}, /* PalmPixDC85 */ {USB_DEVICE(0x06be, 0x0800)}, /* Optimedia */ {USB_DEVICE(0x2899, 0x012c)}, /* Toptro Industrial */ {USB_DEVICE(0x06bd, 0x0404)}, /* Agfa CL20 */ {USB_DEVICE(0x046d, 0x092c)}, /* Logitech QC chat Elch2 */ {USB_DEVICE(0x0c45, 0x607c)}, /* Sonix sn9c102p Hv7131R */ {USB_DEVICE(0x0733, 0x3261)}, /* Concord 3045 spca536a */ {USB_DEVICE(0x0733, 0x1314)}, /* Mercury 2.1MEG Deluxe Classic Cam */ {USB_DEVICE(0x041e, 0x401c)}, /* Creative NX */ {USB_DEVICE(0x041e, 0x4034)}, /* Creative Instant P0620 */ {USB_DEVICE(0x041e, 0x4035)}, /* Creative Instant P0620D */ {USB_DEVICE(0x046d, 0x08ae)}, /* Logitech QuickCam for Notebooks */ {USB_DEVICE(0x055f, 0xd004)}, /* Mustek WCam300 AN */ {USB_DEVICE(0x046d, 0x092b)}, /* Labtec Webcam Plus */ {USB_DEVICE(0x0c45, 0x602e)}, /* Genius VideoCam Messenger */ {USB_DEVICE(0x0c45, 0x602c)}, /* Generic Sonix OV7630 */ {USB_DEVICE(0x093A, 0x050F)}, /* Mars-Semi Pc-Camera */ {USB_DEVICE(0x0458, 0x7006)}, /* Genius Dsc 1.3 Smart */ {USB_DEVICE(0x055f, 0xc630)}, /* Mustek MDC4000 */ {USB_DEVICE(0x046d, 0x08ad)}, /* Logitech QCCommunicate STX */ {USB_DEVICE(0x0c45, 0x602d)}, /* LIC-200 LG */ {USB_DEVICE(0x0c45, 0x6005)}, /* Sweex Tas5110 */ {USB_DEVICE(0x0c45, 0x613c)}, /* Sonix Pccam168 */ {USB_DEVICE(0x0c45, 0x6130)}, /* Sonix Pccam */ {USB_DEVICE(0x0c45, 0x60c0)}, /* Sangha Sn535 */ {USB_DEVICE(0x0c45, 0x60fc)}, /* LG-LIC300 */ {USB_DEVICE(0x0546, 0x3191)}, /* Polaroid Ion 80 */ {USB_DEVICE(0x0ac8, 0x305b)}, /* Z-star Vimicro zc0305b */ {USB_DEVICE(0x0c45, 0x6028)}, /* Sonix Btc Pc380 */ {USB_DEVICE(0x046d, 0x08a9)}, /* Logitech Notebook Deluxe */ {USB_DEVICE(0x046d, 0x08aa)}, /* Labtec Webcam Notebook */ {USB_DEVICE(0x04f1, 0x1001)}, /* JVC GC A50 */ {USB_DEVICE(0x0497, 0xc001)}, /* Smile International */ {USB_DEVICE(0x041e, 0x4012)}, /* PC-Cam350 */ {USB_DEVICE(0x0ac8, 0x303b)}, /* Vimicro 0x303b */ {USB_DEVICE(0x093a, 0x2468)}, /* PAC207 */ {USB_DEVICE(0x093a, 0x2471)}, /* PAC207 Genius VideoCam ge111 */ {USB_DEVICE(0x093a, 0x2460)}, /* PAC207 Qtec Webcam 100 */ {USB_DEVICE(0x0733, 0x3281)}, /* Cyberpix S550V */ {USB_DEVICE(0x093a, 0x2470)}, /* Genius GF112 */ {USB_DEVICE(0x046d, 0x08a6)}, /* Logitech QCim */ {USB_DEVICE(0x08ca, 0x2020)}, /* Aiptek Slim 3000F */ {USB_DEVICE(0x0698, 0x2003)}, /* CTX M730V built in */ {USB_DEVICE(0x0c45, 0x6001)}, /* Genius VideoCAM NB */ {USB_DEVICE(0x041E, 0x4028)}, /* Creative Webcam Vista Plus */ {USB_DEVICE(0x0471, 0x0325)}, /* Philips SPC 200 NC */ {USB_DEVICE(0x0471, 0x0328)}, /* Philips SPC 700 NC */ {USB_DEVICE(0x0c45, 0x6040)}, /* Speed NVC 350K */ {USB_DEVICE(0x055f, 0xc230)}, /* Mustek Digicam 330K */ {USB_DEVICE(0x0c45, 0x6007)}, /* Sonix sn9c101 + Tas5110D */ {USB_DEVICE(0x0471, 0x0327)}, /* Philips SPC 600 NC */ {USB_DEVICE(0x0471, 0x0326)}, /* Philips SPC 300 NC */ {USB_DEVICE(0x0c45, 0x6019)}, /* Generic Sonix OV7630 */ {USB_DEVICE(0x0c45, 0x6024)}, /* Generic Sonix Tas5130c */ {USB_DEVICE(0x0000, 0x0000)}, /* MystFromOri Unknow Camera */ This list represents those cameras that are specifically supported by the driver, and should work to some degree 'out of the box'. A full list of the cameras known to the project maintainers can be found on http://mxhaard.free.fr/spca5xx.html. How do I use it? ================ Well, first you need to compile the driver (see below), then you need to make sure that the v4l infrastructure is set up and then load the driver. After you've done that, any v4l enabled application, such as spcaview, gqcam, xawtv, gnomemeeting, camE etc should work. Supported kernel versions ========================= The driver should compile and run successfully against most stable versions of the official Linux kernel (from ), within the range 2.4.10 to 2.6.14 inclusive. exept kernel 2.4.22, 2.4.23, 2.6.3 -------------------------------------------------------- -Distro patched Kernel should work but are unsupported.- -------------------------------------------------------- Specifically, it has been tested against: 2.4.10 Compiles ok, with 1 warning 2.4.25 Compiles ok, with 1 warning. 2.4.26 Compiles ok, with 1 warning. 2.4.31 Compiles ok, with 1 warning. /lib/modules/2.4.25/build/include/linux/highmem.h: Dans la fonction « bh_kmap »: /lib/modules/2.4.25/build/include/linux/highmem.h:20: attention : usage en arithmétique d'un pointeur de type « void * » Don't care module should load and works fine :) 2.6.7 Compiles ok, with no warnings. 2.6.8.1 Compiles ok, with no warnings. 2.6.9 Compiles ok, with no warnings. 2.6.11.7 Compiles ok, with no warnings. 2.6.12.6 Compiles ok, with no warnings. 2.6.13 Compiles ok, with no warnings. 2.6.14 Compiles ok, with no warnings. 2.6.15 rc6 Compiles ok, with no warnings. 2.6.15.4 Compiles ok, with no warnings. <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< KERNEL 2.4.22 2.4.23 2.6.3 are UNSUPPORTED !!>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Compiling it ============ The driver module can be built without modifying your kernel source tree. Before trying to compile the driver, ensure that you've configured your kernel, and updated the dependencies: 'make [config|menuconfig|xconfig]; make dep'. Make sure, when compiling the driver, you use the same version of compiler as was used to compile your kernel. Not doing so can create incompatible binaries. If you wish to compile the driver against a kernel other than the currently installed one, build the driver with 'make KINCLUDE=/usr/src/linux-/include', or similar. Please note, the default location for the kernel, according to the driver, is /usr/src/linux. To build just the driver, simply use 'make'. Compiling against the linux kernel 2.4 source tree, there should be no warnings at all. This version of the driver offers an automatic installation facility. Use 'make install' to have the driver installed into your kernel modules directory, which is assumed to be /lib/modules//kernel/drivers/usb. is picked up from the currently running kernel, so if that's not the right place, then don't use 'make install'! Making sure the usb and v4l stuff is there ========================================== For the module to function correctly, the video for linux subsystem needs to be loaded. As root, check the output of lsmod for videodev. If its not there, do: modprobe videodev. Also, you need to make sure that the usbcore module is loaded (or compiled into your kernel) and similarly the module appropriate for your usb controller (uhci or ohci). Loading it ========== If you have compiled the module, but haven't done 'make install' you can load the module in the top build directory by doing as root: insmod spca5xx.o If you have made install, do as root: modprobe spca5xx There are several parameter that can be passed in: force_rgb = 1 Set reverse rgb order OffRed = -16 Initial red offset -16 range [-128..+128] OffBlue = -16 Initial blue offset -16 range [-128..+128] OffGreen = -16 Initial green offset -16 range [-128..+128] GRed = 256 Initial gain setting to 1 range [0..512] GBlue = 256 Initial gain setting to 1 range [0..512] GGreen = 256 Initial gain setting to 1 range [0..512] gamma = 3 Set gamma table to 1 range [0..7] usbgrabber = 1 if you use an usb grabber usbid 0x0733:0x430 otherwise usbgrabber=0 debug= <-- set debug level special note for kernel 2.6.x users: you can change the parameters trought sysfs you need to be root close your application echo "1" > /sys/module/spca50x/gamma change the gamma parameters to 1 run your application to read a parameters cat /sys/module/spca50x/gamma *********************************************************************** Remember to use insmod spca5xx.o usbgrabber=1 for an usb grabber usbid 0x733:0x430 for Intel PC Camera Pro set usbgrabber to 0 in webcam mode *********************************************************************** Trying a v4l app ================ Spcaview or Spcagui are designed to test our driver http://mxhaard.free.fr . have a look at the readme for all parameters setting. You can also try gnomemeeting, or any other v4l program, they should Just Work (TM). How about downloading pictures from it, or videos? ================================================== This driver is a v4l driver, whose scope is only streaming video. Support for downloading images and movies for a lot of spca50x cameras is provided as part of the gphoto2 project, which can be found at: . Limitations and known problems ============================== Support for some bridges is not complete yet. Not all resolutions work. The driver as a whole is experimental. What to do if your cam doesn't work ================================== Scenario 1 - bridge not supported yet: If your cam sports a sunplus spca5 something chip which we do not support yet, you are in for some quality entertainment. :) In order to add support for your chip we will need snoops of the windows driver in operation. You can get these using a tool called ,usbsnoop which is free software and can be found here: http://benoit.papillault.free.fr/usbsnoop/. Once you have acquired these, send an email to the mailing list at or, detailing where/how people can download the snoops (eg. website/ftp/email) and if time permits, we'll take a look at them and try to implement support. If you are a developer yourself and want to help, we very much appreciate your contribution and will be happy to explain and answer questions about how the driver works. Scenario 2 - bridge supported, but your cam isn't detected It might just work, but it also might not :). You can try exchanging your usb vendor and product id for those of a camera with the same chip in the source, or ask one of us to do it for you on the list or on irc. If the cam is like the others with the same chip, it might work and your ids can be permanently added. If it doesn't, see scenario one for what to do. Scenario 3 - bridge supported, ids there, but still no luck random list of things to check: - make sure videodev is there; - make sure the usb stuff is working; - make sure the usb subsystem detects your cam; - check syslog, if the driver claims the device; - load the module with debug=4 and check syslog for some extremely verbose debug information; - write to the list or drop into #spca50x on freenode (IRC) and we'll see if we can get you up and running. :) This is a mighty fine project, how can I learn more about it? ============================================================= (nuff said) I want to whine regularly, where can I? ======================================= Please address all support requests to , or use the support/patch/bug tracking features provided by sourceforge on our project page . Who can I blame? ================ spca5xx kernel 2.6.x port and generic chips driver: Current spca5xx maintainer and project lead: Michel Xhaard Michel Xhaard (mxhaard) Reza Jelveh (timebomb) Tomas Groth (tgc) Thomas Kaiser Credits spca5xx: Andrzej Szombierski for the cool spca561 video decompressor:). Bertrik Stikken for the great Sonix video decompressor:). And of course all people reporting webcams (id snoop patch and testing available in the Changelog files) Original spca50x authors: Joel Crisp Current spca50x maintainer and project lead: Miah Gregory Francois Beerten Miah Gregory Till Adam Michel Xhaard (mxhaard) The jpeg decoder was originally written by Michael Schroeder and adjusted to our purposes. All bugs are ours, all features his. Credits (quoting Joel): Thanks to all the authors of the ov511 driver and its ancestors. Thanks to Darrell Scott for debug assistance and suggestions. Thanks to Razvan Surdulescu for kicking me back into action. Thanks to Bill Roehl for traces on the Create and Share (id 0x401). I WILL make this work... or die trying... Thanks to everyone who has tested this driver and given me feedback on it. A note on Sunplus Z-star Vimicro Conexant Sonix Transvision Etoms Pixart Mars-Semi and our interaction with them so far ====================================================================================================================== Several of us have tried, at various times, to obtain information on the bridge and sensors chips from these Manufacturers, but have failed, seemingly due to a lack of interest to cooperate on their part. Therefore, this driver is the result of reverse engineering the protocols and functionality provided by these chips. This limits what we can do, and it limits the quality of the driver. We would much prefer to fully support all the features the chips provide, but without Manufacturers supplying us with the needed specifications and technical documentation, this is unlikely to happen. Both the free software community and Manufacturer(s) could only benefit from improved cooperation in the future. pwcbsd/spca5xx-20060402/README-KERNEL-UPTO-2.6.16000644 000423 000000 00000001343 10546676140 020251 0ustar00luigiwheel000000 000000 Dear users, For some strange reason "Luca Risolia" is setting a lot of drivers inside the main Kernel tree, specially those chips supported by spca5xx. Maybe there are not enought hardware to Hack:) As a consequence, you have to choice between spca5xx or the others. The problem is, hotplug will detect, the in kernel driver and maybe reject spca5xx claim. If you load the spca5xx first with modprobe spca5xx there are no problem. You can also blacklist the in kernel driver:) .If you get problem with the main kernel drivers, don't ask me please, you should ask here to get help: linux-usb-devel@lists.sourceforge.net or here for the v4l2 features :) video4linux-list@redhat.com best regards Michel Xhaard http://mxhaard.free.fr pwcbsd/spca5xx-20060402/README-SONIX000700 000423 000000 00000000736 10546676140 016652 0ustar00luigiwheel000000 000000 Dear spca5xx users, If you have compiled the sn9c102 driver from the main kernel tree, you should get a conflict with spca5xx and sonix sn9c101 or sonix sn9c102 webcams. you can : a)load the spca5xx before plug your webcam with modprobe spca5xx that way the sn9c102 kernel module did not load and you can benefit of the large V4L support b)rename the sn9c102.ko to sn9c102.ko.old in /lib/modules/kernel-name/kernel/drivers/usb/media/ then run the command depmod -ae Regards pwcbsd/spca5xx-20060402/README-TV8532000700 000423 000000 00000001001 10546676140 016607 0ustar00luigiwheel000000 000000 This driver is Experimental : Sometimes colors are strange, Automatic Bayer align need some light to work please move the camera to get more ligth at the top left of your picture. Good setting can be get when loading the module with: modprobe spca5xx GRed=217 GBlue=300 GGreen=224 gamma=4 or using sysfs under 2.6.x echo "4" > /sys/module/spca5xx/gamma echo "217" > /sys/module/spca5xx/GRed should set the gamma parameters to 4 and GRed to 217 read a parameters is easy cat /sys/module/spca5xx/GRed Enjoy !! pwcbsd/spca5xx-20060402/RGB-YUV%2fmodule-setting000700 000423 000000 00000001474 10546676140 021270 0ustar00luigiwheel000000 000000 Some usefull setting for the module: KERNEL 2.6.x ONLY /******************************************************************/ IMPORTANT !!!! you need to close and reopen the device to get the change active. /******************************************************************/ you can also use sysfs kernel 2.6.x like this: echo "4" > /sys/module/spca5xx/gamma echo "217" > /sys/module/spca5xx/GRed should set the gamma parameters to 4 and GRed to 217 The Gimp is usefull to get the good value :) KERNEL 2.6.x AND KERNEL 2.4.x otherwhise load the module for example with : modprobe spca5xx GRed=217 GBlue=300 GGreen=224 gamma=4 or modprobe spca5xx OffRed=16 OffGreen=-32 OffBlue=-16 GBlue=288 GGreen=288 or you can set up that option in the modprobe.conf or modules.conf options spca5xx GRed=217 GBlue=300 GGreen=224 gamma=4 pwcbsd/spca5xx-20060402/cutlog.py000700 000423 000000 00000000564 10546676140 016742 0ustar00luigiwheel000000 000000 #! /usr/bin/env python import syslog journaux = [ "/var/log/messages" ] for journal in journaux: fichier = open(journal) lignes = fichier.readlines() fichier.close souslignes = lignes[-5:] fichier = open(journal, 'w') fichier.writelines(souslignes) fichier.close() syslog.syslog("cutlog.py: réduction des journaux systèmes") pwcbsd/spca5xx-20060402/drivers000755 000423 000000 00000000000 10546676140 016474 5ustar00luigiwheel000000 000000 pwcbsd/spca5xx-20060402/drivers/usb000755 000423 000000 00000000000 10553461761 017264 5ustar00luigiwheel000000 000000 pwcbsd/spca5xx-20060402/drivers/usb/cs2102.h000744 000423 000000 00000026425 10546676140 020441 0ustar00luigiwheel000000 000000 #ifndef CS2102USB_H #define CS2102USB_H /**************************************************************************** # Century Semiconductor CS2102 library # # Copyright (C) 2004 2005 Michel Xhaard mxhaard@magic.fr # # Copyright (C) 2005 Alvaro Salmador naplam33 at msn.com # # This program is free software; you can redistribute it and/or modify # # it under the terms of the GNU General Public License as published by # # the Free Software Foundation; either version 2 of the License, or # # (at your option) any later version. # # # # This program is distributed in the hope that it will be useful, # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # # ****************************************************************************/ static __u16 cs2102_start_data[][3] = { {0xa1, 0x01, 0x0008}, {0xa1, 0x01, 0x0008}, {0xa0, 0x01, 0x0000}, {0xa0, 0x10, 0x0002}, {0xa0, 0x00, 0x0010}, {0xa0, 0x01, 0x0001}, {0xa0, 0x20, 0x0080}, {0xa0, 0x21, 0x0081}, {0xa0, 0x30, 0x0083}, {0xa0, 0x31, 0x0084}, {0xa0, 0x32, 0x0085}, {0xa0, 0x23, 0x0086}, {0xa0, 0x24, 0x0087}, {0xa0, 0x25, 0x0088}, {0xa0, 0xb3, 0x008b}, {0xa0, 0x03, 0x0008}, //00 {0xa0, 0x03, 0x0012}, {0xa0, 0x01, 0x0012}, {0xa0, 0x02, 0x0003}, {0xa0, 0x80, 0x0004}, {0xa0, 0x01, 0x0005}, {0xa0, 0xe0, 0x0006}, {0xa0, 0x00, 0x0098}, {0xa0, 0x00, 0x009a}, {0xa0, 0x00, 0x011a}, {0xa0, 0x00, 0x011c}, {0xa0, 0x02, 0x0092}, {0xa0, 0x08, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x03, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x11, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x12, 0x0092}, {0xa0, 0x89, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x13, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x14, 0x0092}, {0xa0, 0xe9, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x20, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x22, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x0b, 0x0092}, {0xa0, 0x04, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x30, 0x0092}, {0xa0, 0x30, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x31, 0x0092}, {0xa0, 0x30, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x32, 0x0092}, {0xa0, 0x30, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x37, 0x0101}, {0xa0, 0x00, 0x0019}, {0xa0, 0x05, 0x0012}, {0xa0, 0x0d, 0x0100}, {0xa0, 0x06, 0x0189}, {0xa0, 0x03, 0x01c5}, {0xa0, 0x13, 0x01cb}, {0xa0, 0x10, 0x01ae}, {0xa0, 0x08, 0x0250}, {0xa0, 0x08, 0x0301}, {0xa0, 0x68, 0x018d}, {0xa0, 0x00, 0x01ad}, {0xa1, 0x01, 0x0002}, {0xa1, 0x01, 0x0008}, {0xa0, 0x03, 0x0008}, //00 {0xa0, 0x08, 0x01c6}, {0xa1, 0x01, 0x01c8}, {0xa1, 0x01, 0x01c9}, {0xa1, 0x01, 0x01ca}, {0xa0, 0x0f, 0x01cb}, {0xa0, 0x24, 0x0120}, {0xa0, 0x44, 0x0121}, {0xa0, 0x64, 0x0122}, {0xa0, 0x84, 0x0123}, {0xa0, 0x9d, 0x0124}, {0xa0, 0xb2, 0x0125}, {0xa0, 0xc4, 0x0126}, {0xa0, 0xd3, 0x0127}, {0xa0, 0xe0, 0x0128}, {0xa0, 0xeb, 0x0129}, {0xa0, 0xf4, 0x012a}, {0xa0, 0xfb, 0x012b}, {0xa0, 0xff, 0x012c}, {0xa0, 0xff, 0x012d}, {0xa0, 0xff, 0x012e}, {0xa0, 0xff, 0x012f}, {0xa0, 0x18, 0x0130}, {0xa0, 0x20, 0x0131}, {0xa0, 0x20, 0x0132}, {0xa0, 0x1c, 0x0133}, {0xa0, 0x16, 0x0134}, {0xa0, 0x13, 0x0135}, {0xa0, 0x10, 0x0136}, {0xa0, 0x0e, 0x0137}, {0xa0, 0x0b, 0x0138}, {0xa0, 0x09, 0x0139}, {0xa0, 0x07, 0x013a}, {0xa0, 0x06, 0x013b}, {0xa0, 0x00, 0x013c}, {0xa0, 0x00, 0x013d}, {0xa0, 0x00, 0x013e}, {0xa0, 0x01, 0x013f}, {0xa0, 0x58, 0x010a}, {0xa0, 0xf4, 0x010b}, {0xa0, 0xf4, 0x010c}, {0xa0, 0xf4, 0x010d}, {0xa0, 0x58, 0x010e}, {0xa0, 0xf4, 0x010f}, {0xa0, 0xf4, 0x0110}, {0xa0, 0xf4, 0x0111}, {0xa0, 0x58, 0x0112}, {0xa1, 0x01, 0x0180}, {0xa0, 0x00, 0x0180}, {0xa0, 0x00, 0x0019}, {0xa0, 0x23, 0x0092}, {0xa0, 0x01, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x24, 0x0092}, {0xa0, 0x55, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x25, 0x0092}, {0xa0, 0xcc, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x21, 0x0092}, {0xa0, 0x3f, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x02, 0x0190}, {0xa0, 0xab, 0x0191}, {0xa0, 0x98, 0x0192}, {0xa0, 0x00, 0x0195}, {0xa0, 0x30, 0x0196}, {0xa0, 0xd4, 0x0197}, {0xa0, 0x10, 0x018c}, {0xa0, 0x20, 0x018f}, {0xa0, 0x10, 0x01a9}, {0xa0, 0x24, 0x01aa}, {0xa0, 0x39, 0x001d}, {0xa0, 0x70, 0x001e}, {0xa0, 0xb0, 0x001f}, {0xa0, 0xff, 0x0020}, {0xa0, 0x40, 0x0180}, {0xa1, 0x01, 0x0180}, {0xa0, 0x42, 0x0180}, {0xa0, 0x40, 0x0116}, {0xa0, 0x40, 0x0117}, {0xa0, 0x40, 0x0118}, {0, 0, 0} }; static __u16 cs2102_scale_data[][3] = { {0xa1, 0x01, 0x0008}, {0xa1, 0x01, 0x0008}, {0xa0, 0x01, 0x0000}, {0xa0, 0x00, 0x0002}, {0xa0, 0x00, 0x0010}, {0xa0, 0x01, 0x0001}, {0xa0, 0x20, 0x0080}, {0xa0, 0x21, 0x0081}, {0xa0, 0x30, 0x0083}, {0xa0, 0x31, 0x0084}, {0xa0, 0x32, 0x0085}, {0xa0, 0x23, 0x0086}, {0xa0, 0x24, 0x0087}, {0xa0, 0x25, 0x0088}, {0xa0, 0xb3, 0x008b}, {0xa0, 0x03, 0x0008}, //00 {0xa0, 0x03, 0x0012}, {0xa0, 0x01, 0x0012}, {0xa0, 0x02, 0x0003}, {0xa0, 0x80, 0x0004}, {0xa0, 0x01, 0x0005}, {0xa0, 0xe0, 0x0006}, {0xa0, 0x00, 0x0098}, {0xa0, 0x00, 0x009a}, {0xa0, 0x00, 0x011a}, {0xa0, 0x00, 0x011c}, {0xa0, 0x02, 0x0092}, {0xa0, 0x08, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x03, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x11, 0x0092}, {0xa0, 0x01, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x12, 0x0092}, {0xa0, 0x87, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x13, 0x0092}, {0xa0, 0x01, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x14, 0x0092}, {0xa0, 0xe7, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x20, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x22, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x0b, 0x0092}, {0xa0, 0x04, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x30, 0x0092}, {0xa0, 0x30, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x31, 0x0092}, {0xa0, 0x30, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x32, 0x0092}, {0xa0, 0x30, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x77, 0x0101}, {0xa0, 0x00, 0x0019}, {0xa0, 0x05, 0x0012}, {0xa0, 0x0d, 0x0100}, {0xa0, 0x06, 0x0189}, {0xa0, 0x03, 0x01c5}, {0xa0, 0x13, 0x01cb}, {0xa0, 0x15, 0x01ae}, {0xa0, 0x08, 0x0250}, {0xa0, 0x08, 0x0301}, {0xa0, 0x68, 0x018d}, {0xa0, 0x00, 0x01ad}, {0xa1, 0x01, 0x0002}, {0xa1, 0x01, 0x0008}, {0xa0, 0x03, 0x0008}, //00 {0xa0, 0x08, 0x01c6}, {0xa1, 0x01, 0x01c8}, {0xa1, 0x01, 0x01c9}, {0xa1, 0x01, 0x01ca}, {0xa0, 0x0f, 0x01cb}, {0xa0, 0x24, 0x0120}, {0xa0, 0x44, 0x0121}, {0xa0, 0x64, 0x0122}, {0xa0, 0x84, 0x0123}, {0xa0, 0x9d, 0x0124}, {0xa0, 0xb2, 0x0125}, {0xa0, 0xc4, 0x0126}, {0xa0, 0xd3, 0x0127}, {0xa0, 0xe0, 0x0128}, {0xa0, 0xeb, 0x0129}, {0xa0, 0xf4, 0x012a}, {0xa0, 0xfb, 0x012b}, {0xa0, 0xff, 0x012c}, {0xa0, 0xff, 0x012d}, {0xa0, 0xff, 0x012e}, {0xa0, 0xff, 0x012f}, {0xa0, 0x18, 0x0130}, {0xa0, 0x20, 0x0131}, {0xa0, 0x20, 0x0132}, {0xa0, 0x1c, 0x0133}, {0xa0, 0x16, 0x0134}, {0xa0, 0x13, 0x0135}, {0xa0, 0x10, 0x0136}, {0xa0, 0x0e, 0x0137}, {0xa0, 0x0b, 0x0138}, {0xa0, 0x09, 0x0139}, {0xa0, 0x07, 0x013a}, {0xa0, 0x06, 0x013b}, {0xa0, 0x00, 0x013c}, {0xa0, 0x00, 0x013d}, {0xa0, 0x00, 0x013e}, {0xa0, 0x01, 0x013f}, {0xa0, 0x58, 0x010a}, {0xa0, 0xf4, 0x010b}, {0xa0, 0xf4, 0x010c}, {0xa0, 0xf4, 0x010d}, {0xa0, 0x58, 0x010e}, {0xa0, 0xf4, 0x010f}, {0xa0, 0xf4, 0x0110}, {0xa0, 0xf4, 0x0111}, {0xa0, 0x58, 0x0112}, {0xa1, 0x01, 0x0180}, {0xa0, 0x00, 0x0180}, {0xa0, 0x00, 0x0019}, {0xa0, 0x23, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x24, 0x0092}, {0xa0, 0xaa, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x25, 0x0092}, {0xa0, 0xe6, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x21, 0x0092}, {0xa0, 0x3f, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x01, 0x0190}, {0xa0, 0x55, 0x0191}, {0xa0, 0xcc, 0x0192}, {0xa0, 0x00, 0x0195}, {0xa0, 0x18, 0x0196}, {0xa0, 0x6a, 0x0197}, {0xa0, 0x10, 0x018c}, {0xa0, 0x20, 0x018f}, {0xa0, 0x10, 0x01a9}, {0xa0, 0x24, 0x01aa}, {0xa0, 0x3f, 0x001d}, {0xa0, 0xa5, 0x001e}, {0xa0, 0xf0, 0x001f}, {0xa0, 0xff, 0x0020}, {0xa0, 0x40, 0x0180}, {0xa1, 0x01, 0x0180}, {0xa0, 0x42, 0x0180}, {0xa0, 0x40, 0x0116}, {0xa0, 0x40, 0x0117}, {0xa0, 0x40, 0x0118}, {0, 0, 0} }; #endif //CS2102USB_H pwcbsd/spca5xx-20060402/drivers/usb/cx11646.h000755 000423 000000 00000005744 10552522125 020534 0ustar00luigiwheel000000 000000 #ifndef CX11646USB_H #define CX11646USB_H /**************************************************************************** # Connexant Cx11646 library # # Copyright (C) 2004 Michel Xhaard mxhaard@magic.fr # # # # This program is free software; you can redistribute it and/or modify # # it under the terms of the GNU General Public License as published by # # the Free Software Foundation; either version 2 of the License, or # # (at your option) any later version. # # # # This program is distributed in the hope that it will be useful, # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # # ****************************************************************************/ #include "cxlib.h" static int cx11646_init(struct usb_spca50x *spca50x); static void cx11646_start(struct usb_spca50x *spca50x); static void cx11646_stop(struct usb_spca50x *spca50x); /**************************************************************************/ static int cx11646_init(struct usb_spca50x *spca50x) { int err; cx11646_init1(spca50x); err = cx11646_initsize(spca50x); cx11646_fw(spca50x); cx_sensor(spca50x); cx11646_jpegInit(spca50x); return 0; } static void cx11646_start(struct usb_spca50x *spca50x) { int err; err = cx11646_initsize(spca50x); cx11646_fw(spca50x); cx_sensor(spca50x); cx11646_jpeg(spca50x); } static void cx11646_stop(struct usb_spca50x *spca50x) { int retry = 50; __u8 val = 0; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0000, &val, 1); usb_rd_vend_dev(spca50x->dev, 0x00, 0x00, 0x0002, &val, 1); val = 0; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0053, &val, 1); while (retry--) { //usb_rd_vend_dev (spca50x->dev,0x00,0x00,0x0002,&val,1); usb_rd_vend_dev(spca50x->dev, 0x00, 0x00, 0x0053, &val, 1); if (val == 0) break; } val = 0; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0000, &val, 1); usb_rd_vend_dev(spca50x->dev, 0x00, 0x00, 0x0002, &val, 1); val = 0; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0010, &val, 1); usb_rd_vend_dev(spca50x->dev, 0x00, 0x00, 0x0033, &val, 1); val = 0xE0; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x00fc, &val, 1); } #endif //CX11646USB_H pwcbsd/spca5xx-20060402/drivers/usb/cxlib.h000755 000423 000000 00000077234 10552522126 020625 0ustar00luigiwheel000000 000000 /**************************************************************************** # Connexant Cx11646 library # # Copyright (C) 2004 Michel Xhaard mxhaard@magic.fr # # # # This program is free software; you can redistribute it and/or modify # # it under the terms of the GNU General Public License as published by # # the Free Software Foundation; either version 2 of the License, or # # (at your option) any later version. # # # # This program is distributed in the hope that it will be useful, # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # # ****************************************************************************/ static __u8 cx_sensor_init[][4] = { {0x88, 0x11, 0x01, 0x01}, {0x88, 0x12, 0x70, 0x01}, {0x88, 0x0f, 0x00, 0x01}, {0x88, 0x05, 0x01, 0x01}, {0, 0, 0, 0} }; static void cx11646_init1(struct usb_spca50x *spca50x) { __u8 val = 0; int i = 0; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0010, &val, 1); usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0053, &val, 1); usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0052, &val, 1); val = 0x2f; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x009b, &val, 1); val = 0x10; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x009c, &val, 1); usb_rd_vend_dev(spca50x->dev, 0x00, 0x00, 0x0098, &val, 1); val = 0x40; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0098, &val, 1); usb_rd_vend_dev(spca50x->dev, 0x00, 0x00, 0x0099, &val, 1); val = 0x07; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0099, &val, 1); val = 0x40; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0039, &val, 1); val = 0xff; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x003c, &val, 1); val = 0x1f; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x003f, &val, 1); val = 0x40; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x003d, &val, 1); //val= 0x60; //usb_wr_vend_dev(spca50x->dev,0x00,0x00,0x003d,&val,1); usb_rd_vend_dev(spca50x->dev, 0x00, 0x00, 0x0099, &val, 1); //->0x07 while (cx_sensor_init[i][0]) { usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x00e5, cx_sensor_init[i], 1); usb_rd_vend_dev(spca50x->dev, 0x00, 0x00, 0x00e8, &val, 1); // -> 0x00 if (i == 1) { val = 1; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x00ed, &val, 1); usb_rd_vend_dev(spca50x->dev, 0x00, 0x00, 0x00ed, &val, 1); //-> 0x01 } i++; } val = 0x00; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x00c3, &val, 1); } static __u8 cx11646_fw1[][3] = { {0x00, 0x02, 0x00}, {0x01, 0x43, 0x00}, {0x02, 0xA7, 0x00}, {0x03, 0x8B, 0x01}, {0x04, 0xE9, 0x02}, {0x05, 0x08, 0x04}, {0x06, 0x08, 0x05}, {0x07, 0x07, 0x06}, {0x08, 0xE7, 0x06}, {0x09, 0xC6, 0x07}, {0x0A, 0x86, 0x08}, {0x0B, 0x46, 0x09}, {0x0C, 0x05, 0x0A}, {0x0D, 0xA5, 0x0A}, {0x0E, 0x45, 0x0B}, {0x0F, 0xE5, 0x0B}, {0x10, 0x85, 0x0C}, {0x11, 0x25, 0x0D}, {0x12, 0xC4, 0x0D}, {0x13, 0x45, 0x0E}, {0x14, 0xE4, 0x0E}, {0x15, 0x64, 0x0F}, {0x16, 0xE4, 0x0F}, {0x17, 0x64, 0x10}, {0x18, 0xE4, 0x10}, {0x19, 0x64, 0x11}, {0x1A, 0xE4, 0x11}, {0x1B, 0x64, 0x12}, {0x1C, 0xE3, 0x12}, {0x1D, 0x44, 0x13}, {0x1E, 0xC3, 0x13}, {0x1F, 0x24, 0x14}, {0x20, 0xA3, 0x14}, {0x21, 0x04, 0x15}, {0x22, 0x83, 0x15}, {0x23, 0xE3, 0x15}, {0x24, 0x43, 0x16}, {0x25, 0xA4, 0x16}, {0x26, 0x23, 0x17}, {0x27, 0x83, 0x17}, {0x28, 0xE3, 0x17}, {0x29, 0x43, 0x18}, {0x2A, 0xA3, 0x18}, {0x2B, 0x03, 0x19}, {0x2C, 0x63, 0x19}, {0x2D, 0xC3, 0x19}, {0x2E, 0x22, 0x1A}, {0x2F, 0x63, 0x1A}, {0x30, 0xC3, 0x1A}, {0x31, 0x23, 0x1B}, {0x32, 0x83, 0x1B}, {0x33, 0xE2, 0x1B}, {0x34, 0x23, 0x1C}, {0x35, 0x83, 0x1C}, {0x36, 0xE2, 0x1C}, {0x37, 0x23, 0x1D}, {0x38, 0x83, 0x1D}, {0x39, 0xE2, 0x1D}, {0x3A, 0x23, 0x1E}, {0x3B, 0x82, 0x1E}, {0x3C, 0xC3, 0x1E}, {0x3D, 0x22, 0x1F}, {0x3E, 0x63, 0x1F}, {0x3F, 0xC1, 0x1F}, {0, 0, 0} }; static void cx11646_fw(struct usb_spca50x *spca50x) { __u8 val = 0; int i = 0; val = 0x02; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x006a, &val, 1); while (cx11646_fw1[i][1]) { usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x006b, cx11646_fw1[i], 3); i++; } val = 0x00; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x006a, &val, 1); } static __u8 cxsensor[] = { 0x88, 0x12, 0x70, 0x01, 0x88, 0x0d, 0x02, 0x01, 0x88, 0x0f, 0x00, 0x01, 0x88, 0x03, 0x71, 0x01, 0x88, 0x04, 0x00, 0x01, //3 0x88, 0x02, 0x10, 0x01, 0x88, 0x00, 0xD4, 0x01, 0x88, 0x01, 0x01, 0x01, //5 0x88, 0x0B, 0x00, 0x01, 0x88, 0x0A, 0x0A, 0x01, 0x88, 0x00, 0x08, 0x01, 0x88, 0x01, 0x00, 0x01, //8 0x88, 0x05, 0x01, 0x01, 0xA1, 0x18, 0x00, 0x01, 0x00 }; static __u8 reg20[] = { 0x10, 0x42, 0x81, 0x19, 0xd3, 0xff, 0xa7, 0xff }; static __u8 reg28[] = { 0x87, 0x00, 0x87, 0x00, 0x8f, 0xff, 0xea, 0xff }; static __u8 reg10[] = { 0xb1, 0xb1 }; static __u8 reg71a[] = { 0x08, 0x18, 0x0a, 0x1e }; // 640 static __u8 reg71b[] = { 0x04, 0x0c, 0x05, 0x0f }; // 352{0x04,0x0a,0x06,0x12}; //352{0x05,0x0e,0x06,0x11}; //352 static __u8 reg71c[] = { 0x02, 0x07, 0x03, 0x09 }; // 320{0x04,0x0c,0x05,0x0f}; //320 static __u8 reg71d[] = { 0x02, 0x07, 0x03, 0x09 }; // 176 static __u8 reg71e[] = { 0x02, 0x07, 0x03, 0x09 }; // 160 static __u8 reg7b[] = { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff }; static void cx_sensor(struct usb_spca50x *spca50x) { __u8 val = 0; int i = 0; __u8 bufread[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; int length = 0; __u8 *ptsensor = cxsensor; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0020, reg20, 8); usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0028, reg28, 8); usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0010, reg10, 8); val = 0x03; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0092, &val, 1); PDEBUG(3, "spca50x->mode cx11646 %d", spca50x->mode); switch (spca50x->mode) { case 0: usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0071, reg71a, 4); break; case 1: usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0071, reg71b, 4); break; case 2: usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0071, reg71c, 4); break; case 3: usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0071, reg71d, 4); break; case 4: usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0071, reg71e, 4); break; default: usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0071, reg71c, 4); break; } usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x007b, reg7b, 6); val = 0x00; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x00f8, &val, 1); usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0010, reg10, 8); val = 0x41; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0098, &val, 1); for (i = 0; i < 11; i++) { if ((i == 3) || (i == 5) || (i == 8)) { length = 8; } else { length = 4; } usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x00e5, ptsensor, length); if (length == 4) { usb_rd_vend_dev(spca50x->dev, 0x00, 0x00, 0x00e8, &val, 1); } else { usb_rd_vend_dev(spca50x->dev, 0x00, 0x00, 0x00e8, bufread, length); } ptsensor += length; } usb_rd_vend_dev(spca50x->dev, 0x00, 0x00, 0x00e7, bufread, 8); } static __u8 cx_inits_160[] = { 0x81, 0x81, 0xa0, 0x00, 0x78, 0x00, 0x04, 0x01, 0x00, 0x01, 0x01, 0x01, 0x10, 0x00, 0x04, 0x01, 0x65, 0x45, 0x13, 0x1a, 0x2c, 0xdf, 0xb9, 0x81, 0x30, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xe2, 0xff, 0xf1, 0xff, 0xc2, 0xff, 0xbc, 0xff, 0xf5, 0xff, 0x6b, 0xff, 0xf2, 0x01, 0x43, 0x02, 0xe4, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static __u8 cx_inits_176[] = { 0x33, 0x81, 0xB0, 0x00, 0x90, 0x00, 0x0A, 0x03, //176x144 0x00, 0x03, 0x03, 0x03, 0x1B, 0x05, 0x30, 0x03, 0x65, 0x15, 0x18, 0x25, 0x03, 0x25, 0x08, 0x30, 0x3B, 0x25, 0x10, 0x00, 0x04, 0x00, 0x00, 0x00, 0xDC, 0xFF, 0xEE, 0xFF, 0xC5, 0xFF, 0xBF, 0xFF, 0xF7, 0xFF, 0x88, 0xFF, 0x66, 0x02, 0x28, 0x02, 0x1E, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static __u8 cx_inits_320[] = { 0x7f, 0x7f, 0x40, 0x01, 0xf0, 0x00, 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x10, 0x00, 0x02, 0x01, 0x65, 0x45, 0xfa, 0x4c, 0x2c, 0xdf, 0xb9, 0x81, 0x30, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xe2, 0xff, 0xf1, 0xff, 0xc2, 0xff, 0xbc, 0xff, 0xf5, 0xff, 0x6d, 0xff, 0xf6, 0x01, 0x43, 0x02, 0xd3, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static __u8 cx_inits_352[] = { 0x2e, 0x7c, 0x60, 0x01, 0x20, 0x01, 0x05, 0x03, 0x00, 0x06, 0x03, 0x06, 0x1b, 0x10, 0x05, 0x3b, 0x30, 0x25, 0x18, 0x25, 0x08, 0x30, 0x03, 0x25, 0x3b, 0x30, 0x25, 0x1b, 0x10, 0x05, 0x00, 0x00, 0xe3, 0xff, 0xf1, 0xff, 0xc2, 0xff, 0xbc, 0xff, 0xf5, 0xff, 0x6b, 0xff, 0xee, 0x01, 0x43, 0x02, 0xe4, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static __u8 cx_inits_640[] = { 0x7e, 0x7e, 0x80, 0x02, 0xe0, 0x01, 0x01, 0x01, 0x00, 0x02, 0x01, 0x02, 0x10, 0x30, 0x01, 0x01, 0x65, 0x45, 0xf7, 0x52, 0x2c, 0xdf, 0xb9, 0x81, 0x30, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xe2, 0xff, 0xf1, 0xff, 0xc2, 0xff, 0xbc, 0xff, 0xf6, 0xff, 0x7b, 0xff, 0x01, 0x02, 0x43, 0x02, 0x77, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static int cx11646_initsize(struct usb_spca50x *spca50x) { int i; __u8 reg12[] = { 0x08, 0x05, 0x07, 0x04, 0x24 }; __u8 reg17[] = { 0x0a, 0x00, 0xf2, 0x01, 0x0f, 0x00, 0x97, 0x02 }; __u8 *cxinit; __u8 val = 0; switch (spca50x->mode) { case 0: cxinit = cx_inits_640; break; case 1: cxinit = cx_inits_352; break; case 2: cxinit = cx_inits_320; break; case 3: cxinit = cx_inits_176; break; case 4: cxinit = cx_inits_160; break; default: cxinit = cx_inits_320; break; } val = 0x01; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x009a, &val, 1); val = 0x10; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0010, &val, 1); usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0012, reg12, 5); usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0017, reg17, 8); val = 0x00; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x00c0, &val, 1); val = 0x04; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x00c1, &val, 1); val = 0x04; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x00c2, &val, 1); for (i = 0; i < 7; i++) { switch (i) { case 0: usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0061, cxinit, 8); break; case 1: usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x00ca, cxinit, 8); break; case 2: usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x00d2, cxinit, 8); break; case 3: usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x00da, cxinit, 6); break; case 4: usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0041, cxinit, 8); break; case 5: usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0049, cxinit, 8); break; case 6: usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0051, cxinit, 2); break; } if (i < 6) cxinit += 8; //surf trought the table } usb_rd_vend_dev(spca50x->dev, 0x00, 0x00, 0x0010, &val, 1); return (int) val; } static __u8 cx_jpeg_init[][8] = { {0xFF, 0xD8, 0xFF, 0xDB, 0x00, 0x84, 0x00, 0x15}, // 1 {0x0F, 0x10, 0x12, 0x10, 0x0D, 0x15, 0x12, 0x11}, {0x12, 0x18, 0x16, 0x15, 0x19, 0x20, 0x35, 0x22}, {0x20, 0x1D, 0x1D, 0x20, 0x41, 0x2E, 0x31, 0x26}, {0x35, 0x4D, 0x43, 0x51, 0x4F, 0x4B, 0x43, 0x4A}, {0x49, 0x55, 0x5F, 0x79, 0x67, 0x55, 0x5A, 0x73}, {0x5B, 0x49, 0x4A, 0x6A, 0x90, 0x6B, 0x73, 0x7D}, {0x81, 0x88, 0x89, 0x88, 0x52, 0x66, 0x95, 0xA0}, {0x94, 0x84, 0x9E, 0x79, 0x85, 0x88, 0x83, 0x01}, {0x15, 0x0F, 0x10, 0x12, 0x10, 0x0D, 0x15, 0x12}, {0x11, 0x12, 0x18, 0x16, 0x15, 0x19, 0x20, 0x35}, {0x22, 0x20, 0x1D, 0x1D, 0x20, 0x41, 0x2E, 0x31}, {0x26, 0x35, 0x4D, 0x43, 0x51, 0x4F, 0x4B, 0x43}, {0x4A, 0x49, 0x55, 0x5F, 0x79, 0x67, 0x55, 0x5A}, {0x73, 0x5B, 0x49, 0x4A, 0x6A, 0x90, 0x6B, 0x73}, {0x7D, 0x81, 0x88, 0x89, 0x88, 0x52, 0x66, 0x95}, {0xA0, 0x94, 0x84, 0x9E, 0x79, 0x85, 0x88, 0x83}, {0xFF, 0xC4, 0x01, 0xA2, 0x00, 0x00, 0x01, 0x05}, {0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02}, {0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A}, {0x0B, 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01}, {0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05}, {0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x10, 0x00}, {0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05}, {0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7D, 0x01}, {0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21}, {0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22}, {0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, 0x08, 0x23}, {0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24}, {0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17}, {0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29}, {0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A}, {0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A}, {0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A}, {0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A}, {0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A}, {0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A}, {0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99}, {0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8}, {0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7}, {0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6}, {0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5}, {0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2, 0xE3}, {0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1}, {0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9}, {0xFA, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04}, {0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01}, {0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04}, {0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07}, {0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14}, {0x42, 0x91, 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33}, {0x52, 0xF0, 0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16}, {0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, 0x19}, {0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x35, 0x36}, {0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46}, {0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56}, {0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66}, {0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76}, {0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84, 0x85}, {0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94}, {0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3}, {0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2}, {0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA}, {0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9}, {0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8}, {0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7}, {0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6}, {0xF7, 0xF8, 0xF9, 0xFA, 0xFF, 0x20, 0x00, 0x1F}, {0x02, 0x0C, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x11, 0x00, 0x11, 0x22, 0x00, 0x22}, {0x22, 0x11, 0x22, 0x22, 0x11, 0x33, 0x33, 0x11}, {0x44, 0x66, 0x22, 0x55, 0x66, 0xFF, 0xDD, 0x00}, {0x04, 0x00, 0x14, 0xFF, 0xC0, 0x00, 0x11, 0x08}, {0x00, 0xF0, 0x01, 0x40, 0x03, 0x00, 0x21, 0x00}, {0x01, 0x11, 0x01, 0x02, 0x11, 0x01, 0xFF, 0xDA}, {0x00, 0x0C, 0x03, 0x00, 0x00, 0x01, 0x11, 0x02}, {0x11, 0x00, 0x3F, 0x00, 0xFF, 0xD9, 0x00, 0x00} //79 }; static __u8 cxjpeg_640[][8] = { {0xFF, 0xD8, 0xFF, 0xDB, 0x00, 0x84, 0x00, 0x10}, //1 {0x0B, 0x0C, 0x0E, 0x0C, 0x0A, 0x10, 0x0E, 0x0D}, {0x0E, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28, 0x1A}, {0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25, 0x1D}, {0x28, 0x3A, 0x33, 0x3D, 0x3C, 0x39, 0x33, 0x38}, {0x37, 0x40, 0x48, 0x5C, 0x4E, 0x40, 0x44, 0x57}, {0x45, 0x37, 0x38, 0x50, 0x6D, 0x51, 0x57, 0x5F}, {0x62, 0x67, 0x68, 0x67, 0x3E, 0x4D, 0x71, 0x79}, {0x70, 0x64, 0x78, 0x5C, 0x65, 0x67, 0x63, 0x01}, {0x10, 0x0B, 0x0C, 0x0E, 0x0C, 0x0A, 0x10, 0x0E}, {0x0D, 0x0E, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28}, {0x1A, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25}, {0x1D, 0x28, 0x3A, 0x33, 0x3D, 0x3C, 0x39, 0x33}, {0x38, 0x37, 0x40, 0x48, 0x5C, 0x4E, 0x40, 0x44}, {0x57, 0x45, 0x37, 0x38, 0x50, 0x6D, 0x51, 0x57}, {0x5F, 0x62, 0x67, 0x68, 0x67, 0x3E, 0x4D, 0x71}, {0x79, 0x70, 0x64, 0x78, 0x5C, 0x65, 0x67, 0x63}, {0xFF, 0x20, 0x00, 0x1F, 0x00, 0x83, 0x00, 0x00}, {0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00}, {0x11, 0x22, 0x00, 0x22, 0x22, 0x11, 0x22, 0x22}, {0x11, 0x33, 0x33, 0x11, 0x44, 0x66, 0x22, 0x55}, {0x66, 0xFF, 0xDD, 0x00, 0x04, 0x00, 0x28, 0xFF}, {0xC0, 0x00, 0x11, 0x08, 0x01, 0xE0, 0x02, 0x80}, {0x03, 0x00, 0x21, 0x00, 0x01, 0x11, 0x01, 0x02}, {0x11, 0x01, 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x00}, {0x00, 0x01, 0x11, 0x02, 0x11, 0x00, 0x3F, 0x00}, {0xFF, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} //27 }; static __u8 cxjpeg_352[][8] = { {0xFF, 0xD8, 0xFF, 0xDB, 0x00, 0x84, 0x00, 0x0D}, {0x09, 0x09, 0x0B, 0x09, 0x08, 0x0D, 0x0B, 0x0A}, {0x0B, 0x0E, 0x0D, 0x0D, 0x0F, 0x13, 0x1F, 0x14}, {0x13, 0x11, 0x11, 0x13, 0x26, 0x1B, 0x1D, 0x17}, {0x1F, 0x2D, 0x28, 0x30, 0x2F, 0x2D, 0x28, 0x2C}, {0x2B, 0x32, 0x38, 0x48, 0x3D, 0x32, 0x35, 0x44}, {0x36, 0x2B, 0x2C, 0x3F, 0x55, 0x3F, 0x44, 0x4A}, {0x4D, 0x50, 0x51, 0x50, 0x30, 0x3C, 0x58, 0x5F}, {0x58, 0x4E, 0x5E, 0x48, 0x4F, 0x50, 0x4D, 0x01}, {0x0D, 0x09, 0x09, 0x0B, 0x09, 0x08, 0x0D, 0x0B}, {0x0A, 0x0B, 0x0E, 0x0D, 0x0D, 0x0F, 0x13, 0x1F}, {0x14, 0x13, 0x11, 0x11, 0x13, 0x26, 0x1B, 0x1D}, {0x17, 0x1F, 0x2D, 0x28, 0x30, 0x2F, 0x2D, 0x28}, {0x2C, 0x2B, 0x32, 0x38, 0x48, 0x3D, 0x32, 0x35}, {0x44, 0x36, 0x2B, 0x2C, 0x3F, 0x55, 0x3F, 0x44}, {0x4A, 0x4D, 0x50, 0x51, 0x50, 0x30, 0x3C, 0x58}, {0x5F, 0x58, 0x4E, 0x5E, 0x48, 0x4F, 0x50, 0x4D}, {0xFF, 0x20, 0x00, 0x1F, 0x01, 0x83, 0x00, 0x00}, {0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00}, {0x11, 0x22, 0x00, 0x22, 0x22, 0x11, 0x22, 0x22}, {0x11, 0x33, 0x33, 0x11, 0x44, 0x66, 0x22, 0x55}, {0x66, 0xFF, 0xDD, 0x00, 0x04, 0x00, 0x16, 0xFF}, {0xC0, 0x00, 0x11, 0x08, 0x01, 0x20, 0x01, 0x60}, {0x03, 0x00, 0x21, 0x00, 0x01, 0x11, 0x01, 0x02}, {0x11, 0x01, 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x00}, {0x00, 0x01, 0x11, 0x02, 0x11, 0x00, 0x3F, 0x00}, {0xFF, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }; static __u8 cxjpeg_320[][8] = { {0xFF, 0xD8, 0xFF, 0xDB, 0x00, 0x84, 0x00, 0x05}, {0x03, 0x04, 0x04, 0x04, 0x03, 0x05, 0x04, 0x04}, {0x04, 0x05, 0x05, 0x05, 0x06, 0x07, 0x0C, 0x08}, {0x07, 0x07, 0x07, 0x07, 0x0F, 0x0B, 0x0B, 0x09}, {0x0C, 0x11, 0x0F, 0x12, 0x12, 0x11, 0x0F, 0x11}, {0x11, 0x13, 0x16, 0x1C, 0x17, 0x13, 0x14, 0x1A}, {0x15, 0x11, 0x11, 0x18, 0x21, 0x18, 0x1A, 0x1D}, {0x1D, 0x1F, 0x1F, 0x1F, 0x13, 0x17, 0x22, 0x24}, {0x22, 0x1E, 0x24, 0x1C, 0x1E, 0x1F, 0x1E, 0x01}, {0x05, 0x03, 0x04, 0x04, 0x04, 0x03, 0x05, 0x04}, {0x04, 0x04, 0x05, 0x05, 0x05, 0x06, 0x07, 0x0C}, {0x08, 0x07, 0x07, 0x07, 0x07, 0x0F, 0x0B, 0x0B}, {0x09, 0x0C, 0x11, 0x0F, 0x12, 0x12, 0x11, 0x0F}, {0x11, 0x11, 0x13, 0x16, 0x1C, 0x17, 0x13, 0x14}, {0x1A, 0x15, 0x11, 0x11, 0x18, 0x21, 0x18, 0x1A}, {0x1D, 0x1D, 0x1F, 0x1F, 0x1F, 0x13, 0x17, 0x22}, {0x24, 0x22, 0x1E, 0x24, 0x1C, 0x1E, 0x1F, 0x1E}, {0xFF, 0x20, 0x00, 0x1F, 0x02, 0x0C, 0x00, 0x00}, {0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00}, {0x11, 0x22, 0x00, 0x22, 0x22, 0x11, 0x22, 0x22}, {0x11, 0x33, 0x33, 0x11, 0x44, 0x66, 0x22, 0x55}, {0x66, 0xFF, 0xDD, 0x00, 0x04, 0x00, 0x14, 0xFF}, {0xC0, 0x00, 0x11, 0x08, 0x00, 0xF0, 0x01, 0x40}, {0x03, 0x00, 0x21, 0x00, 0x01, 0x11, 0x01, 0x02}, {0x11, 0x01, 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x00}, {0x00, 0x01, 0x11, 0x02, 0x11, 0x00, 0x3F, 0x00}, {0xFF, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} //27 }; static __u8 cxjpeg_176[][8] = { {0xFF, 0xD8, 0xFF, 0xDB, 0x00, 0x84, 0x00, 0x0D}, {0x09, 0x09, 0x0B, 0x09, 0x08, 0x0D, 0x0B, 0x0A}, {0x0B, 0x0E, 0x0D, 0x0D, 0x0F, 0x13, 0x1F, 0x14}, {0x13, 0x11, 0x11, 0x13, 0x26, 0x1B, 0x1D, 0x17}, {0x1F, 0x2D, 0x28, 0x30, 0x2F, 0x2D, 0x28, 0x2C}, {0x2B, 0x32, 0x38, 0x48, 0x3D, 0x32, 0x35, 0x44}, {0x36, 0x2B, 0x2C, 0x3F, 0x55, 0x3F, 0x44, 0x4A}, {0x4D, 0x50, 0x51, 0x50, 0x30, 0x3C, 0x58, 0x5F}, {0x58, 0x4E, 0x5E, 0x48, 0x4F, 0x50, 0x4D, 0x01}, {0x0D, 0x09, 0x09, 0x0B, 0x09, 0x08, 0x0D, 0x0B}, {0x0A, 0x0B, 0x0E, 0x0D, 0x0D, 0x0F, 0x13, 0x1F}, {0x14, 0x13, 0x11, 0x11, 0x13, 0x26, 0x1B, 0x1D}, {0x17, 0x1F, 0x2D, 0x28, 0x30, 0x2F, 0x2D, 0x28}, {0x2C, 0x2B, 0x32, 0x38, 0x48, 0x3D, 0x32, 0x35}, {0x44, 0x36, 0x2B, 0x2C, 0x3F, 0x55, 0x3F, 0x44}, {0x4A, 0x4D, 0x50, 0x51, 0x50, 0x30, 0x3C, 0x58}, {0x5F, 0x58, 0x4E, 0x5E, 0x48, 0x4F, 0x50, 0x4D}, {0xFF, 0x20, 0x00, 0x1F, 0x03, 0xA1, 0x00, 0x00}, {0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00}, {0x11, 0x22, 0x00, 0x22, 0x22, 0x11, 0x22, 0x22}, {0x11, 0x33, 0x33, 0x11, 0x44, 0x66, 0x22, 0x55}, {0x66, 0xFF, 0xDD, 0x00, 0x04, 0x00, 0x0B, 0xFF}, {0xC0, 0x00, 0x11, 0x08, 0x00, 0x90, 0x00, 0xB0}, {0x03, 0x00, 0x21, 0x00, 0x01, 0x11, 0x01, 0x02}, {0x11, 0x01, 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x00}, {0x00, 0x01, 0x11, 0x02, 0x11, 0x00, 0x3F, 0x00}, {0xFF, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }; static __u8 cxjpeg_160[][8] = { {0xFF, 0xD8, 0xFF, 0xDB, 0x00, 0x84, 0x00, 0x0D}, //1 {0x09, 0x09, 0x0B, 0x09, 0x08, 0x0D, 0x0B, 0x0A}, {0x0B, 0x0E, 0x0D, 0x0D, 0x0F, 0x13, 0x1F, 0x14}, {0x13, 0x11, 0x11, 0x13, 0x26, 0x1B, 0x1D, 0x17}, {0x1F, 0x2D, 0x28, 0x30, 0x2F, 0x2D, 0x28, 0x2C}, {0x2B, 0x32, 0x38, 0x48, 0x3D, 0x32, 0x35, 0x44}, {0x36, 0x2B, 0x2C, 0x3F, 0x55, 0x3F, 0x44, 0x4A}, {0x4D, 0x50, 0x51, 0x50, 0x30, 0x3C, 0x58, 0x5F}, {0x58, 0x4E, 0x5E, 0x48, 0x4F, 0x50, 0x4D, 0x01}, {0x0D, 0x09, 0x09, 0x0B, 0x09, 0x08, 0x0D, 0x0B}, {0x0A, 0x0B, 0x0E, 0x0D, 0x0D, 0x0F, 0x13, 0x1F}, {0x14, 0x13, 0x11, 0x11, 0x13, 0x26, 0x1B, 0x1D}, {0x17, 0x1F, 0x2D, 0x28, 0x30, 0x2F, 0x2D, 0x28}, {0x2C, 0x2B, 0x32, 0x38, 0x48, 0x3D, 0x32, 0x35}, {0x44, 0x36, 0x2B, 0x2C, 0x3F, 0x55, 0x3F, 0x44}, {0x4A, 0x4D, 0x50, 0x51, 0x50, 0x30, 0x3C, 0x58}, {0x5F, 0x58, 0x4E, 0x5E, 0x48, 0x4F, 0x50, 0x4D}, {0xFF, 0x20, 0x00, 0x1F, 0x03, 0xB1, 0x00, 0x00}, {0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00}, {0x11, 0x22, 0x00, 0x22, 0x22, 0x11, 0x22, 0x22}, {0x11, 0x33, 0x33, 0x11, 0x44, 0x66, 0x22, 0x55}, {0x66, 0xFF, 0xDD, 0x00, 0x04, 0x00, 0x0A, 0xFF}, {0xC0, 0x00, 0x11, 0x08, 0x00, 0x78, 0x00, 0xA0}, {0x03, 0x00, 0x21, 0x00, 0x01, 0x11, 0x01, 0x02}, {0x11, 0x01, 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x00}, {0x00, 0x01, 0x11, 0x02, 0x11, 0x00, 0x3F, 0x00}, {0xFF, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} //27 }; static __u8 cxjpeg_qtable[][8] = { // 640 take with the zcx30x part {0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x08}, {0x06, 0x06, 0x07, 0x06, 0x05, 0x08, 0x07, 0x07}, {0x07, 0x09, 0x09, 0x08, 0x0a, 0x0c, 0x14, 0x0a}, {0x0c, 0x0b, 0x0b, 0x0c, 0x19, 0x12, 0x13, 0x0f}, {0x14, 0x1d, 0x1a, 0x1f, 0x1e, 0x1d, 0x1a, 0x1c}, {0x1c, 0x20, 0x24, 0x2e, 0x27, 0x20, 0x22, 0x2c}, {0x23, 0x1c, 0x1c, 0x28, 0x37, 0x29, 0x2c, 0x30}, {0x31, 0x34, 0x34, 0x34, 0x1f, 0x27, 0x39, 0x3d}, {0x38, 0x32, 0x3c, 0x2e, 0x33, 0x34, 0x32, 0x01}, {0x09, 0x09, 0x09, 0x0c, 0x0b, 0x0c, 0x18, 0x0a}, {0x0a, 0x18, 0x32, 0x21, 0x1c, 0x21, 0x32, 0x32}, {0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32}, {0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32}, {0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32}, {0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32}, {0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32}, {0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32}, {0xFF, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} //18 }; static void cx11646_jpegInit(struct usb_spca50x *spca50x) { __u8 val = 0; int i = 0; int length = 8; val = 0x01; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x00c0, &val, 1); val = 0x00; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x00c3, &val, 1); val = 0x00; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x00c0, &val, 1); usb_rd_vend_dev(spca50x->dev, 0x00, 0x00, 0x0001, &val, 1); for (i = 0; i < 79; i++) { if (i == 78) length = 6; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0008, cx_jpeg_init[i], length); } usb_rd_vend_dev(spca50x->dev, 0x00, 0x00, 0x0002, &val, 1); val = 0x14; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0055, &val, 1); } static __u8 reg12[] = { 0x0a, 0x05, 0x07, 0x04, 0x19 }; static __u8 regE5_8[] = { 0x88, 0x00, 0xd4, 0x01, 0x88, 0x01, 0x01, 0x01 }; static __u8 regE5a[] = { 0x88, 0x0a, 0x0c, 0x01 }; static __u8 regE5b[] = { 0x88, 0x0b, 0x12, 0x01 }; static __u8 regE5c[] = { 0x88, 0x05, 0x01, 0x01 }; static __u8 reg51[] = { 0x77, 0x03 }; static __u8 reg70 = 0x03; static void cx11646_jpeg(struct usb_spca50x *spca50x) { __u8 val = 0; int i = 0; int length = 8; __u8 Reg55 = 0x14; __u8 bufread[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; int retry = 50; val = 0x01; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x00c0, &val, 1); val = 0x00; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x00c3, &val, 1); val = 0x00; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x00c0, &val, 1); usb_rd_vend_dev(spca50x->dev, 0x00, 0x00, 0x0001, &val, 1); switch (spca50x->mode) { case 0: for (i = 0; i < 27; i++) { if (i == 26) length = 2; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0008, cxjpeg_640[i], length); } Reg55 = 0x28; break; case 1: for (i = 0; i < 27; i++) { if (i == 26) length = 2; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0008, cxjpeg_352[i], length); } Reg55 = 0x16; break; case 2: for (i = 0; i < 27; i++) { if (i == 26) length = 2; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0008, cxjpeg_320[i], length); } Reg55 = 0x14; break; case 3: for (i = 0; i < 27; i++) { if (i == 26) length = 2; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0008, cxjpeg_176[i], length); } Reg55 = 0x0B; break; case 4: for (i = 0; i < 27; i++) { if (i == 26) length = 2; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0008, cxjpeg_160[i], length); } Reg55 = 0x0A; break; default: for (i = 0; i < 27; i++) { if (i == 26) length = 2; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0008, cxjpeg_320[i], length); } Reg55 = 0x14; break; } usb_rd_vend_dev(spca50x->dev, 0x00, 0x00, 0x0002, &val, 1); val = Reg55; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0055, &val, 1); usb_rd_vend_dev(spca50x->dev, 0x00, 0x00, 0x0002, &val, 1); usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0010, reg10, 2); val = 0x02; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0054, &val, 1); val = 0x01; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0054, &val, 1); val = 0x94; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0000, &val, 1); val = 0xc0; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0053, &val, 1); val = 0xe1; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x00fc, &val, 1); val = 0x00; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0000, &val, 1); // wait for completion while (retry--) { usb_rd_vend_dev(spca50x->dev, 0x00, 0x00, 0x0002, &val, 1); // 0x07 until 0x00 if (val == 0x00) break; val = 0x00; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0053, &val, 1); } if (retry == 0) PDEBUG(0, "Damned Errors sending jpeg Table"); // send the qtable now usb_rd_vend_dev(spca50x->dev, 0x00, 0x00, 0x0001, &val, 1); // -> 0x18 length = 8; for (i = 0; i < 18; i++) { if (i == 17) length = 2; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0008, cxjpeg_qtable[i], length); } usb_rd_vend_dev(spca50x->dev, 0x00, 0x00, 0x0002, &val, 1); // 0x00 usb_rd_vend_dev(spca50x->dev, 0x00, 0x00, 0x0053, &val, 1); // 0x00 val = 0x02; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0054, &val, 1); val = 0x01; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0054, &val, 1); val = 0x94; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0000, &val, 1); val = 0xc0; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0053, &val, 1); usb_rd_vend_dev(spca50x->dev, 0x00, 0x00, 0x0038, &val, 1); // 0x40 usb_rd_vend_dev(spca50x->dev, 0x00, 0x00, 0x0038, &val, 1); // 0x40 usb_rd_vend_dev(spca50x->dev, 0x00, 0x00, 0x001f, &val, 1); // 0x38 usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0012, reg12, 5); usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x00e5, regE5_8, 8); usb_rd_vend_dev(spca50x->dev, 0x00, 0x00, 0x00e8, bufread, 8); // usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x00e5, regE5a, 4); usb_rd_vend_dev(spca50x->dev, 0x00, 0x00, 0x00e8, &val, 1); // 0x00 val = 0x01; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x009a, &val, 1); usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x00e5, regE5b, 4); usb_rd_vend_dev(spca50x->dev, 0x00, 0x00, 0x00e8, &val, 1); // 0x00 usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x00e5, regE5c, 4); usb_rd_vend_dev(spca50x->dev, 0x00, 0x00, 0x00e8, &val, 1); // 0x00 usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0051, reg51, 2); usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0010, reg10, 2); usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0070, ®70, 1); } static __u16 cx_getbrightness(struct usb_spca50x *spca50x) { /*FIXME hardcoded as we need to read register of the sensor */ spca50x->brightness = 0xD4 << 8; // 0..256 spca50x->contrast = 0x0c << 11; // 0..0x1f spca50x->colour = 0x03 << 13; // 0..7 return (0xD4 << 8); } static void cx_setbrightness(struct usb_spca50x *spca50x) { __u8 regE5cbx[] = { 0x88, 0x00, 0xd4, 0x01, 0x88, 0x01, 0x01, 0x01 }; __u8 reg51c[] = { 0x77, 0x03 }; __u8 bright = 0; __u8 colors = 0; __u8 val = 0; __u8 bufread[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; bright = (spca50x->brightness >> 8) & 0xff; colors = (spca50x->colour >> 13) & 0x07; regE5cbx[2] = bright; reg51c[1] = colors; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x00e5, regE5cbx, 8); usb_rd_vend_dev(spca50x->dev, 0x00, 0x00, 0x00e8, bufread, 8); // usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x00e5, regE5c, 4); usb_rd_vend_dev(spca50x->dev, 0x00, 0x00, 0x00e8, &val, 1); // 0x00 usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0051, reg51c, 2); usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0010, reg10, 2); usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0070, ®70, 1); } static void cx_setcontrast(struct usb_spca50x *spca50x) { __u8 regE5acx[] = { 0x88, 0x0a, 0x0c, 0x01 }; //seem MSB //__u8 regE5bcx[]={0x88,0x0b,0x12,0x01}; // LSB __u8 reg51c[] = { 0x77, 0x03 }; __u8 contrast = 0; __u8 val = 0; __u8 colors = 0; colors = (spca50x->colour >> 13) & 0x07; reg51c[1] = colors; contrast = (spca50x->contrast >> 11) & 0x1f; if (contrast < 10) contrast = 10; regE5acx[2] = contrast; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x00e5, regE5acx, 4); usb_rd_vend_dev(spca50x->dev, 0x00, 0x00, 0x00e8, &val, 1); // 0x00 usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0051, reg51c, 2); usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0010, reg10, 2); usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, 0x0070, ®70, 1); } pwcbsd/spca5xx-20060402/drivers/usb/dummy_cam.h000744 000423 000000 00000004175 10546676141 021501 0ustar00luigiwheel000000 000000 /******************* Camera Interface ***********************/ /*@ spcaxxx_init send the initialization sequence to the webcam @*/ static int spcaxxx_init(struct usb_spca50x *spca50x); /*@ spcaxxx_start send the sequence to start the stream width height mode method pipe_size should be set @*/ static void spcaxxx_start(struct usb_spca50x *spca50x); /*@ spcaxxx_stop send the sequence to stop the stream on the alternate setting some webcam need to send this sequence on alternate 0 @*/ static void spcaxxx_stop(struct usb_spca50x *spca50x); /*@ spcaxxx_setbrightness set the brightness spca50x->brightness need to be set @*/ static __u16 spcaxxx_setbrightness(struct usb_spca50x *spca50x); /*@ spcaxxx_getbrightness get the brightness in spca50x->brightness @*/ static __u16 spcaxxx_getbrightness(struct usb_spca50x *spca50x); /*@ spcaxxx_setcontrast set the contrast spca50x->contrast need to be set @*/ static __u16 spcaxxx_setcontrast(struct usb_spca50x *spca50x); /*@ spcaxxx_getcontrast get the contrast in spca50x->contrast @*/ static __u16 spcaxxx_getcontrast(struct usb_spca50x *spca50x); /*@ spcaxxx_setcolors set the colors spca50x->colours need to be set @*/ static __u16 spcaxxx_setcolors(struct usb_spca50x *spca50x); /*@ spcaxxx_getcolors get the colors in spca50x->colours @*/ static __u16 spcaxxx_getcolors(struct usb_spca50x *spca50x); /*@ spcaxxx_setexposure set the exposure if possible @*/ static __u16 spcaxxx_setexposure(struct usb_spca50x *spca50x); /*@ spcaxxx_getexposure get the exposure if possible @*/ static __u16 spcaxxx_getexposure(struct usb_spca50x *spca50x); /*@ spca5xxx_setAutobright software Autobrightness not need if the webcam have an hardware mode @*/ static void spcaxxx_setAutobright(struct usb_spca50x *spca50x); /*@ spcaxxx_config input spca50x->bridge, spca50x->sensor, output available palette/size/mode/method, return 0 ok -EINVAL unavailable @*/ static int spcaxxx_config(struct usb_spca50x *spca50x); /*@ spcaxxx_shutdown Close the gpio output line if possible @*/ static void spcaxxx_shutdown(struct usb_spca50x *spca50x); /******************************************************************/ pwcbsd/spca5xx-20060402/drivers/usb/et61xx51.h000755 000423 000000 00000066615 10553461760 021041 0ustar00luigiwheel000000 000000 /*************************************************************************** # Etoms Et61x151 GPL Linux driver by Michel Xhaard (09/09/2004) # This driver is design for embedded Linux hardware but should work happy # on Linux host computer # Etoms compagnies did not provided any help and support # The Linux driver is made by reverse engeneering the usb protocol. # This program is free software; you can redistribute it and/or modify # # it under the terms of the GNU General Public License as published by # # the Free Software Foundation; either version 2 of the License, or # # (at your option) any later version. # # # # This program is distributed in the hope that it will be useful, # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # # ****************************************************************************/ #ifndef ET61XX51_H #define ET61XX51_H #define ETOMS_ALT_SIZE_1000 12 #define ET_GPIO_DIR_CTRL 0x04 //Control IO bit[0..5] (0 in 1 out) #define ET_GPIO_OUT 0x05 // Only IO data #define ET_GPIO_IN 0x06 //Read Only IO data #define ET_RESET_ALL 0x03 #define ET_ClCK 0x01 #define ET_CTRL 0x02 //enable i2c OutClck Powerdown mode #define ET_COMP 0x12 //Compression register #define ET_MAXQt 0x13 #define ET_MINQt 0x14 #define ET_COMP_VAL0 0x02 #define ET_COMP_VAL1 0x03 #define ET_REG1d 0x1d #define ET_REG1e 0x1e #define ET_REG1f 0x1f #define ET_REG20 0x20 #define ET_REG21 0x21 #define ET_REG22 0x22 #define ET_REG23 0x23 #define ET_REG24 0x24 #define ET_REG25 0x25 // base registers for luma calculation #define ET_LUMA_CENTER 0x39 #define ET_G_RED 0x4d #define ET_G_GREEN1 0x4e #define ET_G_BLUE 0x4f #define ET_G_GREEN2 0x50 #define ET_G_GR_H 0x51 #define ET_G_GB_H 0x52 #define ET_O_RED 0x34 #define ET_O_GREEN1 0x35 #define ET_O_BLUE 0x36 #define ET_O_GREEN2 0x37 #define ET_SYNCHRO 0x68 #define ET_STARTX 0x69 #define ET_STARTY 0x6a #define ET_WIDTH_LOW 0x6b #define ET_HEIGTH_LOW 0x6c #define ET_W_H_HEIGTH 0x6d #define ET_REG6e 0x6e //OBW #define ET_REG6f 0x6f //OBW #define ET_REG70 0x70 //OBW_AWB #define ET_REG71 0x71 //OBW_AWB #define ET_REG72 0x72 //OBW_AWB #define ET_REG73 0x73 //Clkdelay ns #define ET_REG74 0x74 // test pattern #define ET_REG75 0x75 // test pattern #define ET_I2C_CLK 0x8c #define ET_PXL_CLK 0x60 #define ET_I2C_BASE 0x89 #define ET_I2C_COUNT 0x8a #define ET_I2C_PREFETCH 0x8b #define ET_I2C_REG 0x88 #define ET_I2C_DATA7 0x87 #define ET_I2C_DATA6 0x86 #define ET_I2C_DATA5 0x85 #define ET_I2C_DATA4 0x84 #define ET_I2C_DATA3 0x83 #define ET_I2C_DATA2 0x82 #define ET_I2C_DATA1 0x81 #define ET_I2C_DATA0 0x80 #define PAS106_REG2 0x02 //pxlClk = systemClk/(reg2) #define PAS106_REG3 0x03 //line/frame H [11..4] #define PAS106_REG4 0x04 //line/frame L [3..0] #define PAS106_REG5 0x05 //exposure time line offset(default 5) #define PAS106_REG6 0x06 //exposure time pixel offset(default 6) #define PAS106_REG7 0x07 //signbit Dac (default 0) #define PAS106_REG9 0x09 #define PAS106_REG0e 0x0e //global gain [4..0](default 0x0e) #define PAS106_REG13 0x13 //end i2c write static __u8 GainRGBG[] = { 0x80, 0x80, 0x80, 0x80, 0x00, 0x00 }; static __u8 I2c2[] = { 0x08, 0x08, 0x08, 0x08, 0x0d }; static __u8 I2c3[] = { 0x12, 0x05 }; static __u8 I2c4[] = { 0x41, 0x08 }; /***************************************************************************/ /******************* Camera Interface ***********************/ static int Et_init(struct usb_spca50x *etx); static void Et_startCamera(struct usb_spca50x *etx); static void Et_stopCamera(struct usb_spca50x *etx); static __u16 Et_setbrightness(struct usb_spca50x *etx); static __u16 Et_getbrightness(struct usb_spca50x *etx); static __u16 Et_setcontrast(struct usb_spca50x *etx); static __u16 Et_getcontrast(struct usb_spca50x *etx); static __u16 Et_setcolors(struct usb_spca50x *etx); static __u16 Et_getcolors(struct usb_spca50x *etx); static void Et_setAutobright(struct usb_spca50x *etx); static int Etxx_config(struct usb_spca50x *spca50x); /******************* Camera private ******************************/ static __u8 Et_getgainG(struct usb_spca50x *etx); static void Et_setgainG(struct usb_spca50x *etx, __u8 gain); static int Et_i2cwrite(struct usb_device *dev, __u8 reg, __u8 * buffer, __u16 length, __u8 mode); static int Et_i2cread(struct usb_device *dev, __u8 reg, __u8 * buffer, __u16 length, __u8 mode); static int Et_WaitStatus(struct usb_device *dev); static int Et_videoOff(struct usb_device *dev); static int Et_videoOn(struct usb_device *dev); static void Et_init1(struct usb_spca50x *etx); /***************************************************************************/ static int Et_i2cwrite(struct usb_device *dev, __u8 reg, __u8 * buffer, __u16 length, __u8 mode) { /* buffer should be [D0..D7] */ int i, j; __u8 base = 0x40; // sensor base for the pas106 __u8 ptchcount = 0; ptchcount = (((length & 0x07) << 4) | (mode & 0x03)); /* set the base address */ Et_RegWrite(dev, 0x0, 0x0, ET_I2C_BASE, &base, 1); /* set count and prefetch */ Et_RegWrite(dev, 0x0, 0x0, ET_I2C_COUNT, &ptchcount, 1); /* set the register base */ Et_RegWrite(dev, 0x0, 0x0, ET_I2C_REG, ®, 1); j = length - 1; for (i = 0; i < length; i++) { Et_RegWrite(dev, 0x0, 0x0, (ET_I2C_DATA0 + j), &buffer[j], 1); j--; } return 0; } static int Et_i2cread(struct usb_device *dev, __u8 reg, __u8 * buffer, __u16 length, __u8 mode) { /* buffer should be [D0..D7] */ int i, j; __u8 base = 0x40; // sensor base for the pas106 __u8 ptchcount = 0; __u8 prefetch = 0x02; ptchcount = (((length & 0x07) << 4) | (mode & 0x03)); /* set the base address */ Et_RegWrite(dev, 0x0, 0x0, ET_I2C_BASE, &base, 1); /* set count and prefetch */ Et_RegWrite(dev, 0x0, 0x0, ET_I2C_COUNT, &ptchcount, 1); /* set the register base */ Et_RegWrite(dev, 0x0, 0x0, ET_I2C_REG, ®, 1); Et_RegWrite(dev, 0x0, 0x0, ET_I2C_PREFETCH, &prefetch, 1); prefetch = 0x00; Et_RegWrite(dev, 0x0, 0x0, ET_I2C_PREFETCH, &prefetch, 1); j = length - 1; for (i = 0; i < length; i++) { Et_RegRead(dev, 0x0, 0x0, (ET_I2C_DATA0 + j), &buffer[j], 1); j--; } return 0; } static int Et_WaitStatus(struct usb_device *dev) { __u8 bytereceived = 0; int retry = 10; while (retry--) { Et_RegRead(dev, 0x0, 0x0, ET_ClCK, &bytereceived, 1); if (bytereceived != 0) return 1; } return 0; } static int Et_videoOff(struct usb_device *dev) { int err = -1; __u8 stopvideo = 0; Et_RegWrite(dev, 0x0, 0x0, ET_GPIO_OUT, &stopvideo, 1); err = Et_WaitStatus(dev); if (!err) PDEBUG(5, "timeout Et_waitStatus VideoON"); return err; } static int Et_videoOn(struct usb_device *dev) { int err = -1; __u8 startvideo = 0x10; //set Bit5 Et_RegWrite(dev, 0x0, 0x0, ET_GPIO_OUT, &startvideo, 1); err = Et_WaitStatus(dev); if (!err) PDEBUG(5, "timeout Et_waitStatus VideoOFF"); return err; } static void Et_init2(struct usb_spca50x *etx) { __u8 value = 0x00; __u8 received = 0x00; __u8 FormLine[] = { 0x84, 0x03, 0x14, 0xf4, 0x01, 0x05 }; PDEBUG(5, "Open Init2 ET"); value = 0x2f; Et_RegWrite(etx->dev, 0x0, 0x0, ET_GPIO_DIR_CTRL, &value, 1); value = 0x10; Et_RegWrite(etx->dev, 0x0, 0x0, ET_GPIO_OUT, &value, 1); Et_RegRead(etx->dev, 0x0, 0x0, ET_GPIO_IN, &received, 1); value = 0x14; //0x14 // 0x16 enabled pattern Et_RegWrite(etx->dev, 0x0, 0x0, ET_ClCK, &value, 1); value = 0x1b; Et_RegWrite(etx->dev, 0x0, 0x0, ET_CTRL, &value, 1); /* compression et subsampling */ if (etx->mode) { value = ET_COMP_VAL1; // 320 } else { value = ET_COMP_VAL0; // 640 } Et_RegWrite(etx->dev, 0x0, 0x0, ET_COMP, &value, 1); value = 0x1f; Et_RegWrite(etx->dev, 0x0, 0x0, ET_MAXQt, &value, 1); value = 0x04; Et_RegWrite(etx->dev, 0x0, 0x0, ET_MINQt, &value, 1); /* undocumented registers */ value = 0xff; Et_RegWrite(etx->dev, 0x0, 0x0, ET_REG1d, &value, 1); value = 0xff; Et_RegWrite(etx->dev, 0x0, 0x0, ET_REG1e, &value, 1); value = 0xff; Et_RegWrite(etx->dev, 0x0, 0x0, ET_REG1f, &value, 1); value = 0x35; Et_RegWrite(etx->dev, 0x0, 0x0, ET_REG20, &value, 1); value = 0x01; Et_RegWrite(etx->dev, 0x0, 0x0, ET_REG21, &value, 1); value = 0x00; Et_RegWrite(etx->dev, 0x0, 0x0, ET_REG22, &value, 1); value = 0xff; Et_RegWrite(etx->dev, 0x0, 0x0, ET_REG23, &value, 1); value = 0xff; Et_RegWrite(etx->dev, 0x0, 0x0, ET_REG24, &value, 1); value = 0x0f; Et_RegWrite(etx->dev, 0x0, 0x0, ET_REG25, &value, 1); /* colors setting */ value = 0x11; Et_RegWrite(etx->dev, 0x0, 0x0, 0x30, &value, 1); //0x30 value = 0x40; Et_RegWrite(etx->dev, 0x0, 0x0, 0x31, &value, 1); value = 0x00; Et_RegWrite(etx->dev, 0x0, 0x0, 0x32, &value, 1); value = 0x00; Et_RegWrite(etx->dev, 0x0, 0x0, ET_O_RED, &value, 1); //0x34 value = 0x00; Et_RegWrite(etx->dev, 0x0, 0x0, ET_O_GREEN1, &value, 1); value = 0x00; Et_RegWrite(etx->dev, 0x0, 0x0, ET_O_BLUE, &value, 1); value = 0x00; Et_RegWrite(etx->dev, 0x0, 0x0, ET_O_GREEN2, &value, 1); /*************/ value = 0x80; Et_RegWrite(etx->dev, 0x0, 0x0, ET_G_RED, &value, 1); //0x4d value = 0x80; Et_RegWrite(etx->dev, 0x0, 0x0, ET_G_GREEN1, &value, 1); value = 0x80; Et_RegWrite(etx->dev, 0x0, 0x0, ET_G_BLUE, &value, 1); value = 0x80; Et_RegWrite(etx->dev, 0x0, 0x0, ET_G_GREEN2, &value, 1); value = 0x00; Et_RegWrite(etx->dev, 0x0, 0x0, ET_G_GR_H, &value, 1); value = 0x00; Et_RegWrite(etx->dev, 0x0, 0x0, ET_G_GB_H, &value, 1); //0x52 /* Window control registers */ value = 0x80; /* use cmc_out */ Et_RegWrite(etx->dev, 0x0, 0x0, 0x61, &value, 1); value = 0x02; Et_RegWrite(etx->dev, 0x0, 0x0, 0x62, &value, 1); value = 0x03; Et_RegWrite(etx->dev, 0x0, 0x0, 0x63, &value, 1); value = 0x14; Et_RegWrite(etx->dev, 0x0, 0x0, 0x64, &value, 1); value = 0x0e; Et_RegWrite(etx->dev, 0x0, 0x0, 0x65, &value, 1); value = 0x02; Et_RegWrite(etx->dev, 0x0, 0x0, 0x66, &value, 1); value = 0x02; Et_RegWrite(etx->dev, 0x0, 0x0, 0x67, &value, 1); /**************************************/ value = 0x8f; Et_RegWrite(etx->dev, 0x0, 0x0, ET_SYNCHRO, &value, 1); //0x68 value = 0x69; //0x6a //0x69 Et_RegWrite(etx->dev, 0x0, 0x0, ET_STARTX, &value, 1); value = 0x0d; //0x0d //0x0c Et_RegWrite(etx->dev, 0x0, 0x0, ET_STARTY, &value, 1); value = 0x80; Et_RegWrite(etx->dev, 0x0, 0x0, ET_WIDTH_LOW, &value, 1); value = 0xe0; Et_RegWrite(etx->dev, 0x0, 0x0, ET_HEIGTH_LOW, &value, 1); value = 0x60; Et_RegWrite(etx->dev, 0x0, 0x0, ET_W_H_HEIGTH, &value, 1); //6d value = 0x86; Et_RegWrite(etx->dev, 0x0, 0x0, ET_REG6e, &value, 1); value = 0x01; Et_RegWrite(etx->dev, 0x0, 0x0, ET_REG6f, &value, 1); value = 0x26; Et_RegWrite(etx->dev, 0x0, 0x0, ET_REG70, &value, 1); value = 0x7a; Et_RegWrite(etx->dev, 0x0, 0x0, ET_REG71, &value, 1); value = 0x01; Et_RegWrite(etx->dev, 0x0, 0x0, ET_REG72, &value, 1); /* Clock Pattern registers ***************** */ value = 0x00; Et_RegWrite(etx->dev, 0x0, 0x0, ET_REG73, &value, 1); value = 0x18; //0x28 Et_RegWrite(etx->dev, 0x0, 0x0, ET_REG74, &value, 1); value = 0x0f; // 0x01 Et_RegWrite(etx->dev, 0x0, 0x0, ET_REG75, &value, 1); /**********************************************/ value = 0x20; Et_RegWrite(etx->dev, 0x0, 0x0, 0x8a, &value, 1); value = 0x0f; Et_RegWrite(etx->dev, 0x0, 0x0, 0x8d, &value, 1); value = 0x08; Et_RegWrite(etx->dev, 0x0, 0x0, 0x8e, &value, 1); /**************************************/ value = 0x08; Et_RegWrite(etx->dev, 0x0, 0x0, 0x03, &value, 1); value = 0x03; Et_RegWrite(etx->dev, 0x0, 0x0, ET_PXL_CLK, &value, 1); value = 0xff; Et_RegWrite(etx->dev, 0x0, 0x0, 0x81, &value, 1); value = 0x00; Et_RegWrite(etx->dev, 0x0, 0x0, 0x80, &value, 1); value = 0xff; Et_RegWrite(etx->dev, 0x0, 0x0, 0x81, &value, 1); value = 0x20; Et_RegWrite(etx->dev, 0x0, 0x0, 0x80, &value, 1); value = 0x01; Et_RegWrite(etx->dev, 0x0, 0x0, 0x03, &value, 1); value = 0x00; Et_RegWrite(etx->dev, 0x0, 0x0, 0x03, &value, 1); value = 0x08; Et_RegWrite(etx->dev, 0x0, 0x0, 0x03, &value, 1); /********************************************/ // Et_RegRead(etx->dev,0x0,0x0,ET_I2C_BASE,&received,1); always 0x40 as the pas106 ??? /* set the sensor */ if (etx->mode) { /* 320 */ value = 0x04; Et_RegWrite(etx->dev, 0x0, 0x0, ET_PXL_CLK, &value, 1); /* now set by fifo the FormatLine setting */ Et_RegWrite(etx->dev, 0x0, 0x0, 0x62, FormLine, 6); } else { /* 640 */ /* setting PixelClock 0x03 mean 24/(3+1) = 6 Mhz 0x05 -> 24/(5+1) = 4 Mhz 0x0b -> 24/(11+1) = 2 Mhz 0x17 -> 24/(23+1) = 1 Mhz */ value = 0x1e; //0x17 Et_RegWrite(etx->dev, 0x0, 0x0, ET_PXL_CLK, &value, 1); /* now set by fifo the FormatLine setting */ Et_RegWrite(etx->dev, 0x0, 0x0, 0x62, FormLine, 6); } value = 0x47; // 0x47; Et_RegWrite(etx->dev, 0x0, 0x0, 0x81, &value, 1); // set exposure times [ 0..0x78] 0->longvalue 0x78->shortvalue value = 0x40; // 0x40; Et_RegWrite(etx->dev, 0x0, 0x0, 0x80, &value, 1); /* Pedro change */ // Brightness change Brith+ decrease value // Brigth- increase value // original value = 0x70; value = 0x30; // 0x20; Et_RegWrite(etx->dev, 0x0, 0x0, 0x81, &value, 1); // set brightness value = 0x20; // 0x20; Et_RegWrite(etx->dev, 0x0, 0x0, 0x80, &value, 1); } static void Et_init1(struct usb_spca50x *etx) { __u8 value = 0x00; __u8 received = 0x00; //__u8 I2c0 [] ={0x0a,0x12,0x05,0x22,0xac,0x00,0x01,0x00}; __u8 I2c0[] = { 0x0a, 0x12, 0x05, 0x6d, 0xcd, 0x00, 0x01, 0x00 }; // try 1/120 0x6d 0xcd 0x40 //__u8 I2c0 [] ={0x0a,0x12,0x05,0xfe,0xfe,0xc0,0x01,0x00}; // 1/60000 hmm ?? PDEBUG(5, "Open Init1 ET"); value = 7; Et_RegWrite(etx->dev, 0x0, 0x0, ET_GPIO_DIR_CTRL, &value, 1); Et_RegRead(etx->dev, 0x0, 0x0, ET_GPIO_IN, &received, 1); value = 1; Et_RegWrite(etx->dev, 0x0, 0x0, ET_RESET_ALL, &value, 1); value = 0; Et_RegWrite(etx->dev, 0x0, 0x0, ET_RESET_ALL, &value, 1); value = 0x10; Et_RegWrite(etx->dev, 0x0, 0x0, ET_ClCK, &value, 1); value = 0x19; Et_RegWrite(etx->dev, 0x0, 0x0, ET_CTRL, &value, 1); /* compression et subsampling */ if (etx->mode) { value = ET_COMP_VAL1; } else { value = ET_COMP_VAL0; } PDEBUG(0, "Open mode %d Compression %d", etx->mode, value); Et_RegWrite(etx->dev, 0x0, 0x0, ET_COMP, &value, 1); value = 0x1d; Et_RegWrite(etx->dev, 0x0, 0x0, ET_MAXQt, &value, 1); value = 0x02; Et_RegWrite(etx->dev, 0x0, 0x0, ET_MINQt, &value, 1); /* undocumented registers */ value = 0xff; Et_RegWrite(etx->dev, 0x0, 0x0, ET_REG1d, &value, 1); value = 0xff; Et_RegWrite(etx->dev, 0x0, 0x0, ET_REG1e, &value, 1); value = 0xff; Et_RegWrite(etx->dev, 0x0, 0x0, ET_REG1f, &value, 1); value = 0x35; Et_RegWrite(etx->dev, 0x0, 0x0, ET_REG20, &value, 1); value = 0x01; Et_RegWrite(etx->dev, 0x0, 0x0, ET_REG21, &value, 1); value = 0x00; Et_RegWrite(etx->dev, 0x0, 0x0, ET_REG22, &value, 1); value = 0xf7; Et_RegWrite(etx->dev, 0x0, 0x0, ET_REG23, &value, 1); value = 0xff; Et_RegWrite(etx->dev, 0x0, 0x0, ET_REG24, &value, 1); value = 0x07; Et_RegWrite(etx->dev, 0x0, 0x0, ET_REG25, &value, 1); /* colors setting */ value = 0x80; Et_RegWrite(etx->dev, 0x0, 0x0, ET_G_RED, &value, 1); value = 0x80; Et_RegWrite(etx->dev, 0x0, 0x0, ET_G_GREEN1, &value, 1); value = 0x80; Et_RegWrite(etx->dev, 0x0, 0x0, ET_G_BLUE, &value, 1); value = 0x80; Et_RegWrite(etx->dev, 0x0, 0x0, ET_G_GREEN2, &value, 1); value = 0x00; Et_RegWrite(etx->dev, 0x0, 0x0, ET_G_GR_H, &value, 1); value = 0x00; Et_RegWrite(etx->dev, 0x0, 0x0, ET_G_GB_H, &value, 1); /* Window control registers */ value = 0xf0; Et_RegWrite(etx->dev, 0x0, 0x0, ET_SYNCHRO, &value, 1); value = 0x56; //0x56 Et_RegWrite(etx->dev, 0x0, 0x0, ET_STARTX, &value, 1); value = 0x05; //0x04 Et_RegWrite(etx->dev, 0x0, 0x0, ET_STARTY, &value, 1); value = 0x60; Et_RegWrite(etx->dev, 0x0, 0x0, ET_WIDTH_LOW, &value, 1); value = 0x20; Et_RegWrite(etx->dev, 0x0, 0x0, ET_HEIGTH_LOW, &value, 1); value = 0x50; Et_RegWrite(etx->dev, 0x0, 0x0, ET_W_H_HEIGTH, &value, 1); value = 0x86; Et_RegWrite(etx->dev, 0x0, 0x0, ET_REG6e, &value, 1); value = 0x01; Et_RegWrite(etx->dev, 0x0, 0x0, ET_REG6f, &value, 1); value = 0x86; Et_RegWrite(etx->dev, 0x0, 0x0, ET_REG70, &value, 1); value = 0x14; Et_RegWrite(etx->dev, 0x0, 0x0, ET_REG71, &value, 1); value = 0x00; Et_RegWrite(etx->dev, 0x0, 0x0, ET_REG72, &value, 1); /* Clock Pattern registers */ value = 0x00; Et_RegWrite(etx->dev, 0x0, 0x0, ET_REG73, &value, 1); value = 0x00; Et_RegWrite(etx->dev, 0x0, 0x0, ET_REG74, &value, 1); value = 0x0a; Et_RegWrite(etx->dev, 0x0, 0x0, ET_REG75, &value, 1); value = 0x04; Et_RegWrite(etx->dev, 0x0, 0x0, ET_I2C_CLK, &value, 1); value = 0x01; Et_RegWrite(etx->dev, 0x0, 0x0, ET_PXL_CLK, &value, 1); /* set the sensor */ if (etx->mode) { I2c0[0] = 0x06; Et_i2cwrite(etx->dev, PAS106_REG2, I2c0, sizeof(I2c0), 1); Et_i2cwrite(etx->dev, PAS106_REG9, I2c2, sizeof(I2c2), 1); value = 0x06; Et_i2cwrite(etx->dev, PAS106_REG2, &value, 1, 1); Et_i2cwrite(etx->dev, PAS106_REG3, I2c3, sizeof(I2c3), 1); //value = 0x1f; value = 0x04; Et_i2cwrite(etx->dev, PAS106_REG0e, &value, 1, 1); } else { I2c0[0] = 0x0a; Et_i2cwrite(etx->dev, PAS106_REG2, I2c0, sizeof(I2c0), 1); Et_i2cwrite(etx->dev, PAS106_REG9, I2c2, sizeof(I2c2), 1); value = 0x0a; Et_i2cwrite(etx->dev, PAS106_REG2, &value, 1, 1); Et_i2cwrite(etx->dev, PAS106_REG3, I2c3, sizeof(I2c3), 1); value = 0x04; //value = 0x10; Et_i2cwrite(etx->dev, PAS106_REG0e, &value, 1, 1); /* bit 2 enable bit 1:2 select 0 1 2 3 value = 0x07;// curve 0 Et_i2cwrite(etx->dev,PAS106_REG0f,&value,1,1); */ } //value = 0x01; //value = 0x22; //Et_i2cwrite(etx->dev,PAS106_REG5,&value,1,1); /* magnetude and sign bit for DAC */ Et_i2cwrite(etx->dev, PAS106_REG7, I2c4, sizeof(I2c4), 1); /* now set by fifo the whole colors setting */ Et_RegWrite(etx->dev, 0x0, 0x0, ET_G_RED, GainRGBG, 6); etx->colour = Et_getcolors(etx); Et_setcolors(etx); } static int Et_init(struct usb_spca50x *etx) { int err = -1; __u8 value = 0x00; PDEBUG(5, "Initialize ET1"); if (etx->desc == Etoms61x151) { Et_init1(etx); } else { Et_init2(etx); } value = 0x08; Et_RegWrite(etx->dev, 0x0, 0x0, ET_RESET_ALL, &value, 1); err = Et_videoOff(etx->dev); PDEBUG(5, "Et_Init_VideoOff %d", err); return 0; } static void Et_startCamera(struct usb_spca50x *etx) { int err = -1; __u8 value = 0x00; if (etx->desc == Etoms61x151) { Et_init1(etx); } else { Et_init2(etx); } value = 0x08; Et_RegWrite(etx->dev, 0x0, 0x0, ET_RESET_ALL, &value, 1); err = Et_videoOn(etx->dev); PDEBUG(5, "Et_VideoOn %d", err); } static void Et_stopCamera(struct usb_spca50x *etx) { int err = -1; err = Et_videoOff(etx->dev); PDEBUG(5, "Et_VideoOff %d", err); } static __u16 Et_setbrightness(struct usb_spca50x *etx) { int i; __u8 brightness = etx->brightness >> 9; for (i = 0; i < 4; i++) { Et_RegWrite(etx->dev, 0x0, 0x0, (ET_O_RED + i), &brightness, 1); } return 0; } static __u16 Et_getbrightness(struct usb_spca50x *etx) { int i; int brightness = 0; __u8 value = 0; for (i = 0; i < 4; i++) { Et_RegRead(etx->dev, 0x0, 0x0, (ET_O_RED + i), &value, 1); brightness += value; } return (brightness << 6); } static __u16 Et_setcontrast(struct usb_spca50x *etx) { __u8 RGBG[] = { 0x80, 0x80, 0x80, 0x80, 0x00, 0x00 }; __u8 contrast = etx->contrast >> 8; memset(RGBG, contrast, sizeof(RGBG) - 2); Et_RegWrite(etx->dev, 0x0, 0x0, ET_G_RED, RGBG, 6); return 0; } static __u16 Et_getcontrast(struct usb_spca50x *etx) { int i; int contrast = 0; __u8 value = 0; for (i = 0; i < 4; i++) { Et_RegRead(etx->dev, 0x0, 0x0, (ET_G_RED + i), &value, 1); contrast += value; } return (contrast << 6); } static __u8 Et_getgainG(struct usb_spca50x *etx) { __u8 value = 0; if (PWC_SC(etx)->pwc_info.sensor == SENSOR_PAS106) { Et_i2cread(etx->dev, PAS106_REG0e, &value, 1, 1); PDEBUG(5, "Etoms gain G %d", value); return value; } else { return 0x1f; } } static void Et_setgainG(struct usb_spca50x *etx, __u8 gain) { __u8 i2cflags = 0x01; if (PWC_SC(etx)->pwc_info.sensor == SENSOR_PAS106) { Et_i2cwrite(etx->dev, PAS106_REG13, &i2cflags, 1, 3); Et_i2cwrite(etx->dev, PAS106_REG0e, &gain, 1, 1); #if 0 Et_i2cwrite(etx->dev, 0x09, &gain, 1, 1); Et_i2cwrite(etx->dev, 0x0a, &gain, 1, 1); Et_i2cwrite(etx->dev, 0x0b, &gain, 1, 1); Et_i2cwrite(etx->dev, 0x0c, &gain, 1, 1); #endif } } #define BLIMIT(bright) (__u8)((bright>0x1F)?0x1f:((bright<4)?3:bright)) #define LIMIT(color) (unsigned char)((color>0xFF)?0xff:((color<0)?0:color)) static void Et_setAutobright(struct usb_spca50x *etx) { __u8 GRBG[] = { 0, 0, 0, 0 }; __u8 luma = 0; __u8 luma_mean = 128; __u8 luma_delta = 20; __u8 spring = 4; int Gbright = 0; __u8 r, g, b; Gbright = Et_getgainG(etx); Et_RegRead(etx->dev, 0x0, 0x0, ET_LUMA_CENTER, GRBG, 4); g = (GRBG[0] + GRBG[3]) >> 1; r = GRBG[1]; b = GRBG[2]; r = ((r << 8) - (r << 4) - (r << 3)) >> 10; b = ((b << 7) >> 10); g = ((g << 9) + (g << 7) + (g << 5)) >> 10; luma = LIMIT(r + g + b); PDEBUG(5, "Etoms luma G %d", luma); if ((luma < (luma_mean - luma_delta)) || (luma > (luma_mean + luma_delta))) { Gbright += ((luma_mean - luma) >> spring); Gbright = BLIMIT(Gbright); PDEBUG(5, "Etoms Gbright %d", Gbright); Et_setgainG(etx, (__u8) Gbright); } } #undef BLIMIT #undef LIMIT static __u16 Et_setcolors(struct usb_spca50x *etx) { static __u8 I2cc[] = { 0x05, 0x02, 0x02, 0x05, 0x0d }; __u8 i2cflags = 0x01; //__u8 green = 0; __u8 colors = (etx->colour >> 12) & 0x0f; I2cc[3] = colors; //red I2cc[0] = 15 - colors; //blue // green = 15 - ((((7*I2cc[0]) >> 2 ) + I2cc[3]) >> 1); // I2cc[1] = I2cc[2] = green; if (PWC_SC(etx)->pwc_info.sensor == SENSOR_PAS106) { Et_i2cwrite(etx->dev, PAS106_REG13, &i2cflags, 1, 3); Et_i2cwrite(etx->dev, PAS106_REG9, I2cc, sizeof(I2cc), 1); } //PDEBUG(5 , "Etoms red %d blue %d green %d",I2cc[3],I2cc[0],green); return 0; } static __u16 Et_getcolors(struct usb_spca50x *etx) { //__u8 valblue = 0; __u8 valred = 0; if (PWC_SC(etx)->pwc_info.sensor == SENSOR_PAS106) { //Et_i2cread(etx->dev,PAS106_REG9,&valblue,1,1); Et_i2cread(etx->dev, PAS106_REG9 + 3, &valred, 1, 1); return (((valred) & 0x0f) << 12); } return 0; } static void set_EtxxVGA(struct usb_spca50x *spca50x) { memset(spca50x->mode_cam, 0x00, TOTMODE * sizeof(struct mwebcam)); #if 0 spca50x->mode_cam[VGA].width = 640; spca50x->mode_cam[VGA].height = 480; spca50x->mode_cam[VGA].t_palette = P_RAW | P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[VGA].pipe = 1000; spca50x->mode_cam[VGA].method = 0; spca50x->mode_cam[VGA].mode = 0; spca50x->mode_cam[PAL].width = 384; spca50x->mode_cam[PAL].height = 288; spca50x->mode_cam[PAL].t_palette = P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[PAL].pipe = 1000; spca50x->mode_cam[PAL].method = 1; spca50x->mode_cam[PAL].mode = 0; spca50x->mode_cam[SIF].width = 352; spca50x->mode_cam[SIF].height = 288; spca50x->mode_cam[SIF].t_palette = P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[SIF].pipe = 1000; spca50x->mode_cam[SIF].method = 1; spca50x->mode_cam[SIF].mode = 0; #endif /* XXX actually this is SIF */ spca50x->mode_cam[CIF].width = 320; spca50x->mode_cam[CIF].height = 240; spca50x->mode_cam[CIF].t_palette = P_RAW | P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[CIF].pipe = 1000; spca50x->mode_cam[CIF].method = 0; spca50x->mode_cam[CIF].mode = 1; spca50x->mode_cam[QPAL].width = 192; spca50x->mode_cam[QPAL].height = 144; spca50x->mode_cam[QPAL].t_palette = P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[QPAL].pipe = 1000; spca50x->mode_cam[QPAL].method = 1; spca50x->mode_cam[QPAL].mode = 1; /* XXX actually QCIF and QSIF are swapped */ spca50x->mode_cam[QSIF].width = 176; spca50x->mode_cam[QSIF].height = 144; spca50x->mode_cam[QSIF].t_palette = P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[QSIF].pipe = 1000; spca50x->mode_cam[QSIF].method = 1; spca50x->mode_cam[QSIF].mode = 1; spca50x->mode_cam[QCIF].width = 160; spca50x->mode_cam[QCIF].height = 120; spca50x->mode_cam[QCIF].t_palette = P_RAW | P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[QCIF].pipe = 1000; spca50x->mode_cam[QCIF].method = 1; spca50x->mode_cam[QCIF].mode = 1; } static void set_EtxxSIF(struct usb_spca50x *spca50x) { memset(spca50x->mode_cam, 0x00, TOTMODE * sizeof(struct mwebcam)); spca50x->mode_cam[SIF].width = 352; spca50x->mode_cam[SIF].height = 288; spca50x->mode_cam[SIF].t_palette = P_RAW | P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[SIF].pipe = 1000; spca50x->mode_cam[SIF].method = 0; spca50x->mode_cam[SIF].mode = 0; spca50x->mode_cam[CIF].width = 320; spca50x->mode_cam[CIF].height = 240; spca50x->mode_cam[CIF].t_palette = P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[CIF].pipe = 1000; spca50x->mode_cam[CIF].method = 1; spca50x->mode_cam[CIF].mode = 0; spca50x->mode_cam[QPAL].width = 192; spca50x->mode_cam[QPAL].height = 144; spca50x->mode_cam[QPAL].t_palette = P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[QPAL].pipe = 1000; spca50x->mode_cam[QPAL].method = 1; spca50x->mode_cam[QPAL].mode = 0; spca50x->mode_cam[QSIF].width = 176; spca50x->mode_cam[QSIF].height = 144; spca50x->mode_cam[QSIF].t_palette = P_RAW | P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[QSIF].pipe = 1000; spca50x->mode_cam[QSIF].method = 0; spca50x->mode_cam[QSIF].mode = 1; spca50x->mode_cam[QCIF].width = 160; spca50x->mode_cam[QCIF].height = 120; spca50x->mode_cam[QCIF].t_palette = P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[QCIF].pipe = 1000; spca50x->mode_cam[QCIF].method = 1; spca50x->mode_cam[QCIF].mode = 1; } static int Etxx_config(struct usb_spca50x *spca50x) { switch (PWC_SC(spca50x)->pwc_info.sensor) { case SENSOR_TAS5130C: set_EtxxVGA(spca50x); break; case SENSOR_PAS106: set_EtxxSIF(spca50x); break; default: return -EINVAL; break; } return 0; } #endif /* ET61XX51_H */ pwcbsd/spca5xx-20060402/drivers/usb/hdcs2020.h000744 000423 000000 00000057552 10546676141 020762 0ustar00luigiwheel000000 000000 /**************************************************************************** # Agilent HDCS2020 library # # Copyright (C) 2004 Michel Xhaard mxhaard@magic.fr # # # # This program is free software; you can redistribute it and/or modify # # it under the terms of the GNU General Public License as published by # # the Free Software Foundation; either version 2 of the License, or # # (at your option) any later version. # # # # This program is distributed in the hope that it will be useful, # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # # ****************************************************************************/ /* play poker with registers at your own risk !! */ static __u16 hdcs2020xx_start_data[][3] = { {0xA0, 0x01, 0x0000}, {0xA0, 0x03, 0x0008}, {0xA0, 0x0E, 0x0010}, {0xA0, 0x10, 0x0002}, {0xA0, 0x02, 0x0003}, {0xA0, 0x80, 0x0004}, {0xA0, 0x01, 0x0005}, {0xA0, 0xD0, 0x0006}, //D0 ?? E0 did not start {0xA0, 0x01, 0x0001}, {0xA0, 0x03, 0x0012}, {0xA0, 0x01, 0x0012}, {0xA0, 0x08, 0x008D}, {0xA0, 0x08, 0x0098}, {0xA0, 0x02, 0x009A}, {0xA0, 0x08, 0x011A}, {0xA0, 0x02, 0x011C}, {0xA0, 0x01, 0x009B}, {0xA0, 0xD8, 0x009C}, {0xA0, 0x02, 0x009D}, {0xA0, 0x88, 0x009E}, {0xA0, 0x02, 0x0092}, {0xA0, 0x02, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x07, 0x0092}, {0xA0, 0x06, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x08, 0x0092}, {0xA0, 0x02, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x09, 0x0092}, {0xA0, 0x06, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x0A, 0x0092}, {0xA0, 0x01, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x0B, 0x0092}, {0xA0, 0x01, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x0C, 0x0092}, {0xA0, 0x08, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x0D, 0x0092}, {0xA0, 0x00, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x10, 0x0092}, {0xA0, 0x00, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x12, 0x0092}, {0xA0, 0x05, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x13, 0x0092}, {0xA0, 0x63, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x15, 0x0092}, {0xA0, 0x70, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x37, 0x0101}, {0xA0, 0x0D, 0x0100}, {0xA0, 0x06, 0x0189}, {0xA0, 0x00, 0x01AD}, {0xA0, 0x03, 0x01C5}, {0xA0, 0x13, 0x01CB}, {0xA0, 0x08, 0x0250}, {0xA0, 0x08, 0x0301}, {0xA0, 0x70, 0x018D}, {0xA1, 0x01, 0x0002}, {0xA1, 0x01, 0x0008}, {0xA0, 0x03, 0x0008}, {0xA0, 0x04, 0x01C6}, {0xA1, 0x01, 0x01C8}, {0xA1, 0x01, 0x01C9}, {0xA1, 0x01, 0x01CA}, {0xA0, 0x07, 0x01CB}, {0xA0, 0x11, 0x0120}, {0xA0, 0x37, 0x0121}, {0xA0, 0x58, 0x0122}, {0xA0, 0x79, 0x0123}, {0xA0, 0x91, 0x0124}, {0xA0, 0xA6, 0x0125}, {0xA0, 0xB8, 0x0126}, {0xA0, 0xC7, 0x0127}, {0xA0, 0xD3, 0x0128}, {0xA0, 0xDE, 0x0129}, {0xA0, 0xE6, 0x012A}, {0xA0, 0xED, 0x012B}, {0xA0, 0xF3, 0x012C}, {0xA0, 0xF8, 0x012D}, {0xA0, 0xFB, 0x012E}, {0xA0, 0xFF, 0x012F}, {0xA0, 0x26, 0x0130}, {0xA0, 0x23, 0x0131}, {0xA0, 0x20, 0x0132}, {0xA0, 0x1C, 0x0133}, {0xA0, 0x16, 0x0134}, {0xA0, 0x13, 0x0135}, {0xA0, 0x10, 0x0136}, {0xA0, 0x0D, 0x0137}, {0xA0, 0x0B, 0x0138}, {0xA0, 0x09, 0x0139}, {0xA0, 0x07, 0x013A}, {0xA0, 0x06, 0x013B}, {0xA0, 0x05, 0x013C}, {0xA0, 0x04, 0x013D}, {0xA0, 0x03, 0x013E}, {0xA0, 0x02, 0x013F}, {0xA0, 0x4C, 0x010A}, {0xA0, 0xF5, 0x010B}, {0xA0, 0xFF, 0x010C}, {0xA0, 0xF9, 0x010D}, {0xA0, 0x51, 0x010E}, {0xA0, 0xF5, 0x010F}, {0xA0, 0xFB, 0x0110}, {0xA0, 0xED, 0x0111}, {0xA0, 0x5F, 0x0112}, {0xA1, 0x01, 0x0180}, {0xA0, 0x00, 0x0180}, {0xA0, 0x00, 0x0019}, {0xA0, 0x20, 0x0087}, {0xA0, 0x21, 0x0088}, {0xA0, 0x20, 0x0092}, {0xA0, 0x04, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x21, 0x0092}, {0xA0, 0x3D, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x03, 0x0092}, {0xA0, 0x41, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x04, 0x0092}, {0xA0, 0x10, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x05, 0x0092}, {0xA0, 0x3D, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x0E, 0x0092}, {0xA0, 0x01, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x0F, 0x0092}, {0xA0, 0x00, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x14, 0x01A9}, {0xA0, 0x24, 0x01AA}, {0xA0, 0x00, 0x0190}, {0xA0, 0x04, 0x0191}, {0xA0, 0x3D, 0x0192}, {0xA0, 0x00, 0x0195}, {0xA0, 0x00, 0x0196}, {0xA0, 0x9B, 0x0197}, {0xA0, 0x10, 0x018C}, {0xA0, 0x20, 0x018F}, {0xA0, 0x41, 0x001D}, {0xA0, 0x6F, 0x001E}, {0xA0, 0xAD, 0x001F}, {0xA0, 0xFF, 0x0020}, {0xA0, 0x0F, 0x0087}, {0xA0, 0x0E, 0x0088}, {0xA0, 0x40, 0x0180}, {0xA1, 0x01, 0x0195}, {0xA1, 0x01, 0x0196}, {0xA1, 0x01, 0x0197}, {0xA0, 0x3D, 0x0192}, {0xA0, 0x04, 0x0191}, {0xA0, 0x00, 0x0190}, {0xA0, 0x1D, 0x0116}, {0xA0, 0x40, 0x0117}, {0xA0, 0x85, 0x0118}, {0xA1, 0x01, 0x0116}, {0xA1, 0x01, 0x0118}, {0xA1, 0x01, 0x0180}, {0xA0, 0x42, 0x0180}, {0xA0, 0x1D, 0x0116}, {0xA0, 0x40, 0x0117}, {0xA0, 0x85, 0x0118}, {0xA1, 0x01, 0x0116}, {0xA1, 0x01, 0x0118}, //{ 0xA0, 0x02, 0x0008}, {0xA0, 0x00, 0x0007}, {0, 0, 0} }; static __u16 hdcs2020xx_scale_data[][3] = { {0xA0, 0x01, 0x0000}, {0xA0, 0x03, 0x0008}, {0xA0, 0x0E, 0x0010}, {0xA0, 0x00, 0x0002}, {0xA0, 0x02, 0x0003}, {0xA0, 0x80, 0x0004}, {0xA0, 0x01, 0x0005}, {0xA0, 0xE0, 0x0006}, {0xA0, 0x01, 0x0001}, {0xA0, 0x03, 0x0012}, {0xA0, 0x01, 0x0012}, {0xA0, 0x08, 0x008D}, {0xA0, 0x00, 0x0098}, {0xA0, 0x03, 0x009A}, {0xA0, 0x00, 0x011A}, {0xA0, 0x03, 0x011C}, {0xA0, 0x01, 0x009B}, {0xA0, 0xE6, 0x009C}, {0xA0, 0x02, 0x009D}, {0xA0, 0x86, 0x009E}, {0xA0, 0x02, 0x0092}, {0xA0, 0x02, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x07, 0x0092}, {0xA0, 0x06, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x08, 0x0092}, {0xA0, 0x02, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x09, 0x0092}, {0xA0, 0x06, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x0A, 0x0092}, {0xA0, 0x01, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x0B, 0x0092}, {0xA0, 0x01, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x0C, 0x0092}, {0xA0, 0x08, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x0D, 0x0092}, {0xA0, 0x00, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x10, 0x0092}, {0xA0, 0x00, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x12, 0x0092}, {0xA0, 0x05, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x13, 0x0092}, {0xA0, 0x63, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x15, 0x0092}, {0xA0, 0x70, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0xB7, 0x0101}, {0xA0, 0x0D, 0x0100}, {0xA0, 0x06, 0x0189}, {0xA0, 0x00, 0x01AD}, {0xA0, 0x03, 0x01C5}, {0xA0, 0x13, 0x01CB}, {0xA0, 0x08, 0x0250}, {0xA0, 0x08, 0x0301}, {0xA0, 0x70, 0x018D}, {0xA1, 0x01, 0x0002}, {0xA1, 0x01, 0x0008}, {0xA0, 0x03, 0x0008}, {0xA0, 0x04, 0x01C6}, {0xA1, 0x01, 0x01C8}, {0xA1, 0x01, 0x01C9}, {0xA1, 0x01, 0x01CA}, {0xA0, 0x07, 0x01CB}, {0xA0, 0x11, 0x0120}, {0xA0, 0x37, 0x0121}, {0xA0, 0x58, 0x0122}, {0xA0, 0x79, 0x0123}, {0xA0, 0x91, 0x0124}, {0xA0, 0xA6, 0x0125}, {0xA0, 0xB8, 0x0126}, {0xA0, 0xC7, 0x0127}, {0xA0, 0xD3, 0x0128}, {0xA0, 0xDE, 0x0129}, {0xA0, 0xE6, 0x012A}, {0xA0, 0xED, 0x012B}, {0xA0, 0xF3, 0x012C}, {0xA0, 0xF8, 0x012D}, {0xA0, 0xFB, 0x012E}, {0xA0, 0xFF, 0x012F}, {0xA0, 0x26, 0x0130}, {0xA0, 0x23, 0x0131}, {0xA0, 0x20, 0x0132}, {0xA0, 0x1C, 0x0133}, {0xA0, 0x16, 0x0134}, {0xA0, 0x13, 0x0135}, {0xA0, 0x10, 0x0136}, {0xA0, 0x0D, 0x0137}, {0xA0, 0x0B, 0x0138}, {0xA0, 0x09, 0x0139}, {0xA0, 0x07, 0x013A}, {0xA0, 0x06, 0x013B}, {0xA0, 0x05, 0x013C}, {0xA0, 0x04, 0x013D}, {0xA0, 0x03, 0x013E}, {0xA0, 0x02, 0x013F}, #if 0 {0xA0, 0x4C, 0x010A}, {0xA0, 0xF5, 0x010B}, {0xA0, 0xFF, 0x010C}, {0xA0, 0xF9, 0x010D}, {0xA0, 0x51, 0x010E}, {0xA0, 0xF5, 0x010F}, {0xA0, 0xFB, 0x0110}, {0xA0, 0xED, 0x0111}, {0xA0, 0x5F, 0x0112}, #endif {0xA0, 0x60, 0x010A}, {0xA0, 0xff, 0x010B}, {0xA0, 0xff, 0x010C}, {0xA0, 0xFf, 0x010D}, {0xA0, 0x60, 0x010E}, {0xA0, 0xFf, 0x010F}, {0xA0, 0xFf, 0x0110}, {0xA0, 0xff, 0x0111}, {0xA0, 0x60, 0x0112}, {0xA1, 0x01, 0x0180}, {0xA0, 0x00, 0x0180}, {0xA0, 0x00, 0x0019}, {0xA0, 0x20, 0x0087}, {0xA0, 0x21, 0x0088}, {0xA0, 0x20, 0x0092}, {0xA0, 0x02, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x21, 0x0092}, {0xA0, 0x1B, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x03, 0x0092}, {0xA0, 0x44, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x04, 0x0092}, {0xA0, 0x08, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x05, 0x0092}, {0xA0, 0x1B, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x0E, 0x0092}, {0xA0, 0x01, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x0F, 0x0092}, {0xA0, 0x00, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x14, 0x01A9}, {0xA0, 0x24, 0x01AA}, {0xA0, 0x00, 0x0190}, {0xA0, 0x02, 0x0191}, {0xA0, 0x1B, 0x0192}, {0xA0, 0x00, 0x0195}, {0xA0, 0x00, 0x0196}, {0xA0, 0x4D, 0x0197}, {0xA0, 0x10, 0x018C}, {0xA0, 0x20, 0x018F}, {0xA0, 0x44, 0x001D}, {0xA0, 0x6F, 0x001E}, {0xA0, 0xAD, 0x001F}, {0xA0, 0xEB, 0x0020}, {0xA0, 0x0F, 0x0087}, {0xA0, 0x0E, 0x0088}, {0xA0, 0x40, 0x0180}, {0xA1, 0x01, 0x0195}, {0xA1, 0x01, 0x0196}, {0xA1, 0x01, 0x0197}, {0xA0, 0x1B, 0x0192}, {0xA0, 0x02, 0x0191}, {0xA0, 0x00, 0x0190}, {0xA0, 0x1D, 0x0116}, {0xA0, 0x40, 0x0117}, {0xA0, 0x99, 0x0118}, {0xA1, 0x01, 0x0116}, {0xA1, 0x01, 0x0118}, {0xA1, 0x01, 0x0180}, {0xA0, 0x42, 0x0180}, {0xA0, 0x1D, 0x0116}, {0xA0, 0x40, 0x0117}, {0xA0, 0x99, 0x0118}, // { 0xA0, 0x02, 0x0008}, {0xA0, 0x00, 0x0007}, //{ 0xA0, 0x18, 0x00FE}, {0, 0, 0} }; static __u16 hdcs2020xb_start_data[][3] = { {0xa0, 0x01, 0x0000}, {0xa0, 0x11, 0x0002}, {0xa0, 0x03, 0x0008}, // qtable 0x05 {0xa0, 0x08, 0x0010}, {0xa0, 0x02, 0x0003}, {0xa0, 0x80, 0x0004}, {0xa0, 0x01, 0x0005}, {0xa0, 0xe0, 0x0006}, {0xa0, 0x01, 0x0001}, {0xa0, 0x03, 0x0012}, {0xa0, 0x01, 0x0012}, {0xa0, 0x00, 0x0098}, {0xa0, 0x00, 0x009a}, {0xa0, 0x00, 0x011a}, {0xa0, 0x00, 0x011c}, {0xa0, 0xe8, 0x009c}, {0xa0, 0x88, 0x009e}, {0xa0, 0x1c, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x0a, 0x0092}, {0xa0, 0x01, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x0b, 0x0092}, {0xa0, 0x06, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x0c, 0x0092}, {0xa0, 0x7b, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x0d, 0x0092}, {0xa0, 0xa7, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x03, 0x0092}, {0xa0, 0xfb, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x05, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x06, 0x0092}, {0xa0, 0x03, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x09, 0x0092}, {0xa0, 0x08, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x0f, 0x0092}, // set sensor gain {0xa0, 0x18, 0x0093}, //0x18 {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x10, 0x0092}, {0xa0, 0x18, 0x0093}, //0x18 {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x11, 0x0092}, {0xa0, 0x18, 0x0093}, //0x18 {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x12, 0x0092}, {0xa0, 0x18, 0x0093}, //0x18 {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x15, 0x0092}, {0xa0, 0x4e, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x1c, 0x0092}, {0xa0, 0x04, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0xb7, 0x0101}, {0xa0, 0x05, 0x0012}, {0xa0, 0x70, 0x018d}, {0xa0, 0x0d, 0x0100}, {0xa0, 0x06, 0x0189}, {0xa0, 0x03, 0x01c5}, {0xa0, 0x13, 0x01cb}, {0xa0, 0x08, 0x0250}, {0xa0, 0x08, 0x0301}, {0xa1, 0x01, 0x0002}, {0xa1, 0x01, 0x0008}, {0xa1, 0x01, 0x0180}, {0xa0, 0x02, 0x0180}, {0xa0, 0x40, 0x0116}, {0xa0, 0x40, 0x0117}, {0xa0, 0x40, 0x0118}, {0xa1, 0x01, 0x0008}, {0xa0, 0x03, 0x0008}, // {0xa0, 0x08, 0x01c6}, {0xa1, 0x01, 0x01c8}, {0xa1, 0x01, 0x01c9}, {0xa1, 0x01, 0x01ca}, {0xa0, 0x0f, 0x01cb}, {0xa0, 0x13, 0x0120}, {0xa0, 0x38, 0x0121}, {0xa0, 0x59, 0x0122}, {0xa0, 0x79, 0x0123}, {0xa0, 0x92, 0x0124}, {0xa0, 0xa7, 0x0125}, {0xa0, 0xb9, 0x0126}, {0xa0, 0xc8, 0x0127}, {0xa0, 0xd4, 0x0128}, {0xa0, 0xdf, 0x0129}, {0xa0, 0xe7, 0x012a}, {0xa0, 0xee, 0x012b}, {0xa0, 0xf4, 0x012c}, {0xa0, 0xf9, 0x012d}, {0xa0, 0xfc, 0x012e}, {0xa0, 0xff, 0x012f}, {0xa0, 0x26, 0x0130}, {0xa0, 0x22, 0x0131}, {0xa0, 0x20, 0x0132}, {0xa0, 0x1c, 0x0133}, {0xa0, 0x16, 0x0134}, {0xa0, 0x13, 0x0135}, {0xa0, 0x10, 0x0136}, {0xa0, 0x0d, 0x0137}, {0xa0, 0x0b, 0x0138}, {0xa0, 0x09, 0x0139}, {0xa0, 0x07, 0x013a}, {0xa0, 0x06, 0x013b}, {0xa0, 0x05, 0x013c}, {0xa0, 0x04, 0x013d}, {0xa0, 0x03, 0x013e}, {0xa0, 0x02, 0x013f}, /********************/ {0xa0, 0x66, 0x010a}, //66 {0xa0, 0xed, 0x010b}, //ed {0xa0, 0xed, 0x010c}, //ed {0xa0, 0xed, 0x010d}, //ed {0xa0, 0x66, 0x010e}, //66 {0xa0, 0xed, 0x010f}, //ed {0xa0, 0xed, 0x0110}, //ed {0xa0, 0xed, 0x0111}, //ed {0xa0, 0x66, 0x0112}, //66 {0xa1, 0x01, 0x0180}, {0xa0, 0x00, 0x0180}, {0xa0, 0x00, 0x0019}, {0xa0, 0x13, 0x0092}, {0xa0, 0x31, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x14, 0x0092}, {0xa0, 0x01, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x0e, 0x0092}, {0xa0, 0x04, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x19, 0x0092}, {0xa0, 0xcd, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x00, 0x0190}, {0xa0, 0x02, 0x0191}, {0xa0, 0x62, 0x0192}, {0xa0, 0x00, 0x0195}, {0xa0, 0x00, 0x0196}, {0xa0, 0x3d, 0x0197}, {0xa0, 0x10, 0x018c}, {0xa0, 0x20, 0x018f}, /********************/ {0xa0, 0x0c, 0x01a9}, //0x14 {0xa0, 0x28, 0x01aa}, /********************/ {0xa0, 0x04, 0x001d}, {0xa0, 0x18, 0x001e}, {0xa0, 0x2c, 0x001f}, {0xa0, 0x41, 0x0020}, {0xa0, 0x60, 0x011d}, {0xa0, 0x42, 0x0180}, {0xa1, 0x01, 0x0180}, {0xa0, 0x42, 0x0180}, {0xa0, 0x40, 0x0116}, {0xa0, 0x40, 0x0117}, {0xa0, 0x40, 0x0118}, {0, 0, 0} }; static __u16 hdcs2020xb_scale_data[][3] = { {0xa0, 0x01, 0x0000}, {0xa0, 0x00, 0x0002}, {0xa0, 0x03, 0x0008}, {0xa0, 0x08, 0x0010}, {0xa0, 0x02, 0x0003}, {0xa0, 0x80, 0x0004}, {0xa0, 0x01, 0x0005}, {0xa0, 0xe0, 0x0006}, {0xa0, 0x01, 0x0001}, {0xa0, 0x03, 0x0012}, {0xa0, 0x01, 0x0012}, {0xa0, 0x00, 0x0098}, {0xa0, 0x00, 0x009a}, {0xa0, 0x00, 0x011a}, {0xa0, 0x00, 0x011c}, {0xa0, 0xe8, 0x009c}, {0xa0, 0x88, 0x009e}, {0xa0, 0x1c, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x0a, 0x0092}, {0xa0, 0x01, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x0b, 0x0092}, {0xa0, 0x06, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x0c, 0x0092}, {0xa0, 0x7a, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x0d, 0x0092}, {0xa0, 0xa7, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x03, 0x0092}, {0xa0, 0xfb, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x05, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x06, 0x0092}, {0xa0, 0x03, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x09, 0x0092}, {0xa0, 0x08, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, #if 0 {0xa0, 0x0f, 0x0092}, // set sensor gain {0xa0, 0x4f, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x10, 0x0092}, {0xa0, 0x4f, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x11, 0x0092}, {0xa0, 0x4f, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x12, 0x0092}, {0xa0, 0x4f, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, #endif {0xa0, 0x0f, 0x0092}, // original setting {0xa0, 0x18, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x10, 0x0092}, {0xa0, 0x18, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x11, 0x0092}, {0xa0, 0x18, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x12, 0x0092}, {0xa0, 0x18, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, /**********************/ {0xa0, 0x15, 0x0092}, {0xa0, 0x4e, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x1c, 0x0092}, {0xa0, 0x04, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0xf7, 0x0101}, {0xa0, 0x05, 0x0012}, {0xa0, 0x70, 0x018d}, {0xa0, 0x0d, 0x0100}, {0xa0, 0x06, 0x0189}, {0xa0, 0x03, 0x01c5}, {0xa0, 0x13, 0x01cb}, {0xa0, 0x08, 0x0250}, {0xa0, 0x08, 0x0301}, {0xa1, 0x01, 0x0002}, {0xa1, 0x01, 0x0008}, {0xa1, 0x01, 0x0180}, {0xa0, 0x02, 0x0180}, {0xa0, 0x40, 0x0116}, {0xa0, 0x40, 0x0117}, {0xa0, 0x40, 0x0118}, {0xa1, 0x01, 0x0008}, {0xa0, 0x03, 0x0008}, {0xa0, 0x08, 0x01c6}, {0xa1, 0x01, 0x01c8}, {0xa1, 0x01, 0x01c9}, {0xa1, 0x01, 0x01ca}, {0xa0, 0x0f, 0x01cb}, {0xa0, 0x13, 0x0120}, {0xa0, 0x38, 0x0121}, {0xa0, 0x59, 0x0122}, {0xa0, 0x79, 0x0123}, {0xa0, 0x92, 0x0124}, {0xa0, 0xa7, 0x0125}, {0xa0, 0xb9, 0x0126}, {0xa0, 0xc8, 0x0127}, {0xa0, 0xd4, 0x0128}, {0xa0, 0xdf, 0x0129}, {0xa0, 0xe7, 0x012a}, {0xa0, 0xee, 0x012b}, {0xa0, 0xf4, 0x012c}, {0xa0, 0xf9, 0x012d}, {0xa0, 0xfc, 0x012e}, {0xa0, 0xff, 0x012f}, {0xa0, 0x26, 0x0130}, {0xa0, 0x22, 0x0131}, {0xa0, 0x20, 0x0132}, {0xa0, 0x1c, 0x0133}, {0xa0, 0x16, 0x0134}, {0xa0, 0x13, 0x0135}, {0xa0, 0x10, 0x0136}, {0xa0, 0x0d, 0x0137}, {0xa0, 0x0b, 0x0138}, {0xa0, 0x09, 0x0139}, {0xa0, 0x07, 0x013a}, {0xa0, 0x06, 0x013b}, {0xa0, 0x05, 0x013c}, {0xa0, 0x04, 0x013d}, {0xa0, 0x03, 0x013e}, {0xa0, 0x02, 0x013f}, {0xa0, 0x66, 0x010a}, {0xa0, 0xed, 0x010b}, {0xa0, 0xed, 0x010c}, {0xa0, 0xed, 0x010d}, {0xa0, 0x66, 0x010e}, {0xa0, 0xed, 0x010f}, {0xa0, 0xed, 0x0110}, {0xa0, 0xed, 0x0111}, {0xa0, 0x66, 0x0112}, {0xa1, 0x01, 0x0180}, {0xa0, 0x00, 0x0180}, {0xa0, 0x00, 0x0019}, /**** set exposure ***/ {0xa0, 0x13, 0x0092}, {0xa0, 0x31, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x14, 0x0092}, {0xa0, 0x01, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x0e, 0x0092}, {0xa0, 0x04, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x19, 0x0092}, {0xa0, 0xcd, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x00, 0x0190}, {0xa0, 0x02, 0x0191}, {0xa0, 0x62, 0x0192}, {0xa0, 0x00, 0x0195}, {0xa0, 0x00, 0x0196}, {0xa0, 0x3d, 0x0197}, {0xa0, 0x10, 0x018c}, {0xa0, 0x20, 0x018f}, {0xa0, 0x0c, 0x01a9}, {0xa0, 0x28, 0x01aa}, {0xa0, 0x04, 0x001d}, {0xa0, 0x18, 0x001e}, {0xa0, 0x2c, 0x001f}, {0xa0, 0x41, 0x0020}, {0xa0, 0x60, 0x011d}, {0xa0, 0x42, 0x0180}, {0xa1, 0x01, 0x0180}, {0xa0, 0x42, 0x0180}, {0xa0, 0x40, 0x0116}, {0xa0, 0x40, 0x0117}, {0xa0, 0x40, 0x0118}, {0, 0, 0} }; pwcbsd/spca5xx-20060402/drivers/usb/hv7131b.h000744 000423 000000 00000025622 10546676141 020621 0ustar00luigiwheel000000 000000 /**************************************************************************** # Hynix HV7131b library # # Copyright (C) 2004 Michel Xhaard mxhaard@magic.fr # # # # This program is free software; you can redistribute it and/or modify # # it under the terms of the GNU General Public License as published by # # the Free Software Foundation; either version 2 of the License, or # # (at your option) any later version. # # # # This program is distributed in the hope that it will be useful, # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # # ****************************************************************************/ static __u16 hv7131bxx_start_data[][3] = { {0xA0, 0x01, 0x0000}, {0xA0, 0x10, 0x0002}, {0xA0, 0x00, 0x0010}, {0xA0, 0x01, 0x0001}, {0xA0, 0x77, 0x0101}, {0xA0, 0x03, 0x0008}, //00 {0xA0, 0x03, 0x0012}, {0xA0, 0x01, 0x0012}, {0xA0, 0x02, 0x0003}, {0xA0, 0x80, 0x0004}, {0xA0, 0x01, 0x0005}, {0xA0, 0xE0, 0x0006}, {0xA0, 0x00, 0x0098}, {0xA0, 0x00, 0x009A}, {0xA0, 0x00, 0x011A}, {0xA0, 0x00, 0x011C}, {0xA0, 0x30, 0x0092}, {0xA0, 0x2D, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x01, 0x0092}, {0xA0, 0x05, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x11, 0x0092}, {0xA0, 0x00, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x13, 0x0092}, // { 0xA0, 0x00, 0x0093}, {0xA0, 0x01, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x14, 0x0092}, {0xA0, 0x01, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x15, 0x0092}, {0xA0, 0xE8, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x16, 0x0092}, {0xA0, 0x02, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x17, 0x0092}, {0xA0, 0x86, 0x0093}, //88 {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x31, 0x0092}, {0xA0, 0x38, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x32, 0x0092}, {0xA0, 0x38, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x33, 0x0092}, {0xA0, 0x38, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x5B, 0x0092}, {0xA0, 0x01, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x00, 0x0019}, {0xA0, 0x05, 0x0012}, {0xA0, 0x0D, 0x0100}, {0xA0, 0x68, 0x018D}, {0xA0, 0x60, 0x01A8}, {0xA0, 0x00, 0x01AD}, {0xA0, 0xC0, 0x019B}, {0xA0, 0xA0, 0x019C}, {0xA0, 0x02, 0x0188}, {0xA0, 0x06, 0x0189}, {0xA0, 0x03, 0x01C5}, {0xA0, 0x13, 0x01CB}, {0xA0, 0x08, 0x0250}, {0xA0, 0x08, 0x0301}, {0xA0, 0x02, 0x0092}, // { 0xA0, 0x80, 0x0093}, {0xA0, 0x90, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA1, 0x01, 0x0002}, {0xA0, 0x00, 0x0092}, {0xA0, 0x02, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA1, 0x01, 0x0095}, {0xA1, 0x01, 0x0096}, {0xA1, 0x01, 0x0008}, {0xA0, 0x03, 0x0008}, //00 {0xA0, 0x08, 0x01C6}, {0xA1, 0x01, 0x01C8}, {0xA1, 0x01, 0x01C9}, {0xA1, 0x01, 0x01CA}, {0xA0, 0x0F, 0x01CB}, {0xA0, 0x50, 0x010A}, {0xA0, 0xF8, 0x010B}, {0xA0, 0xF8, 0x010C}, {0xA0, 0xF8, 0x010D}, {0xA0, 0x50, 0x010E}, {0xA0, 0xF8, 0x010F}, {0xA0, 0xF8, 0x0110}, {0xA0, 0xF8, 0x0111}, {0xA0, 0x50, 0x0112}, {0xA1, 0x01, 0x0180}, {0xA0, 0x10, 0x0180}, {0xA0, 0x00, 0x0019}, {0xA0, 0x25, 0x0092}, {0xA0, 0x07, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x26, 0x0092}, {0xA0, 0xA1, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x27, 0x0092}, {0xA0, 0x20, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x20, 0x0092}, {0xA0, 0x00, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x21, 0x0092}, {0xA0, 0xA0, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x22, 0x0092}, {0xA0, 0x16, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x23, 0x0092}, {0xA0, 0x40, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x10, 0x0190}, //2F {0xA0, 0x04, 0x0191}, //4d {0xA0, 0x60, 0x0192}, {0xA0, 0x01, 0x0195}, {0xA0, 0x86, 0x0196}, {0xA0, 0xA0, 0x0197}, {0xA0, 0x07, 0x018C}, {0xA0, 0x0F, 0x018F}, {0xA0, 0x18, 0x01A9}, {0xA0, 0x24, 0x01AA}, {0xA0, 0x00, 0x001D}, {0xA0, 0xA0, 0x001E}, {0xA0, 0x16, 0x001F}, {0xA0, 0x40, 0x0020}, {0xA0, 0x60, 0x011D}, {0xA1, 0x01, 0x001D}, {0xA1, 0x01, 0x001E}, {0xA1, 0x01, 0x001F}, {0xA1, 0x01, 0x0020}, {0xA0, 0x40, 0x0180}, {0xA1, 0x01, 0x0180}, {0xA0, 0x42, 0x0180}, {0xA0, 0x40, 0x0116}, {0xA0, 0x40, 0x0117}, {0xA0, 0x40, 0x0118}, // { 0xA0, 0x02, 0x0008}, {0, 0, 0} }; static __u16 hv7131bxx_scale_data[][3] = { {0xA0, 0x01, 0x0000}, {0xA0, 0x00, 0x0002}, {0xA0, 0x00, 0x0010}, {0xA0, 0x01, 0x0001}, {0xA0, 0x37, 0x0101}, {0xA0, 0x03, 0x0008}, //00 {0xA0, 0x03, 0x0012}, {0xA0, 0x01, 0x0012}, {0xA0, 0x02, 0x0003}, {0xA0, 0x80, 0x0004}, {0xA0, 0x01, 0x0005}, {0xA0, 0xE0, 0x0006}, {0xA0, 0x00, 0x0098}, {0xA0, 0x00, 0x009A}, {0xA0, 0x00, 0x011A}, {0xA0, 0x00, 0x011C}, {0xA0, 0x30, 0x0092}, {0xA0, 0x2D, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x01, 0x0092}, {0xA0, 0x05, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x11, 0x0092}, {0xA0, 0x01, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x13, 0x0092}, //{ 0xA0, 0x01, 0x0093}, {0xA0, 0x00, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x14, 0x0092}, {0xA0, 0x01, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x15, 0x0092}, {0xA0, 0xE6, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x16, 0x0092}, {0xA0, 0x02, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x17, 0x0092}, {0xA0, 0x86, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x31, 0x0092}, {0xA0, 0x38, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x32, 0x0092}, {0xA0, 0x38, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x33, 0x0092}, {0xA0, 0x38, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x5B, 0x0092}, {0xA0, 0x01, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x00, 0x0019}, {0xA0, 0x05, 0x0012}, {0xA0, 0x0D, 0x0100}, {0xA0, 0x70, 0x018D}, {0xA0, 0x60, 0x01A8}, {0xA0, 0x00, 0x01AD}, {0xA0, 0xC0, 0x019B}, {0xA0, 0xA0, 0x019C}, {0xA0, 0x02, 0x0188}, {0xA0, 0x06, 0x0189}, {0xA0, 0x03, 0x01C5}, {0xA0, 0x13, 0x01CB}, {0xA0, 0x08, 0x0250}, {0xA0, 0x08, 0x0301}, {0xA0, 0x02, 0x0092}, // { 0xA0, 0x80, 0x0093}, {0xA0, 0x90, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA1, 0x01, 0x0002}, {0xA0, 0x00, 0x0092}, {0xA0, 0x02, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA1, 0x01, 0x0095}, {0xA1, 0x01, 0x0096}, {0xA1, 0x01, 0x0008}, {0xA0, 0x03, 0x0008}, //00 {0xA0, 0x08, 0x01C6}, {0xA1, 0x01, 0x01C8}, {0xA1, 0x01, 0x01C9}, {0xA1, 0x01, 0x01CA}, {0xA0, 0x0F, 0x01CB}, {0xA0, 0x50, 0x010A}, {0xA0, 0xF8, 0x010B}, {0xA0, 0xF8, 0x010C}, {0xA0, 0xF8, 0x010D}, {0xA0, 0x50, 0x010E}, {0xA0, 0xF8, 0x010F}, {0xA0, 0xF8, 0x0110}, {0xA0, 0xF8, 0x0111}, {0xA0, 0x50, 0x0112}, {0xA1, 0x01, 0x0180}, {0xA0, 0x10, 0x0180}, {0xA0, 0x00, 0x0019}, {0xA0, 0x25, 0x0092}, {0xA0, 0x07, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x26, 0x0092}, {0xA0, 0xA1, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x27, 0x0092}, {0xA0, 0x20, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x20, 0x0092}, {0xA0, 0x00, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x21, 0x0092}, {0xA0, 0x40, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x22, 0x0092}, {0xA0, 0x13, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x23, 0x0092}, {0xA0, 0x4C, 0x0093}, {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, {0xA0, 0x10, 0x0190}, //2f {0xA0, 0x04, 0x0191}, //4d {0xA0, 0x60, 0x0192}, //60 {0xA0, 0x00, 0x0195}, {0xA0, 0xC3, 0x0196}, {0xA0, 0x50, 0x0197}, {0xA0, 0x0C, 0x018C}, {0xA0, 0x18, 0x018F}, {0xA0, 0x18, 0x01A9}, {0xA0, 0x24, 0x01AA}, {0xA0, 0x00, 0x001D}, {0xA0, 0x40, 0x001E}, {0xA0, 0x13, 0x001F}, {0xA0, 0x4C, 0x0020}, {0xA0, 0x60, 0x011D}, {0xA1, 0x01, 0x001D}, {0xA1, 0x01, 0x001E}, {0xA1, 0x01, 0x001F}, {0xA1, 0x01, 0x0020}, {0xA0, 0x40, 0x0180}, {0xA1, 0x01, 0x0180}, {0xA0, 0x42, 0x0180}, {0xA0, 0x40, 0x0116}, {0xA0, 0x40, 0x0117}, {0xA0, 0x40, 0x0118}, // { 0xA0, 0x02, 0x0008}, {0, 0, 0} }; pwcbsd/spca5xx-20060402/drivers/usb/hv7131c.h000744 000423 000000 00000020045 10546676141 020614 0ustar00luigiwheel000000 000000 /**************************************************************************** # Hynix HV7131b library # # Copyright (C) 2004 Michel Xhaard mxhaard@magic.fr # # # # This program is free software; you can redistribute it and/or modify # # it under the terms of the GNU General Public License as published by # # the Free Software Foundation; either version 2 of the License, or # # (at your option) any later version. # # # # This program is distributed in the hope that it will be useful, # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # # ****************************************************************************/ static __u16 hv7131cxx_start_data[][3] = { {0xa0, 0x01, 0x0000}, {0xa0, 0x10, 0x0002}, {0xa0, 0x01, 0x0010}, {0xa0, 0x01, 0x0001}, {0xa0, 0x77, 0x0101}, {0xa0, 0x03, 0x0008}, {0xa0, 0x05, 0x0012}, {0xa0, 0x07, 0x0012}, {0xa0, 0x02, 0x0003}, {0xa0, 0x80, 0x0004}, {0xa0, 0x01, 0x0005}, {0xa0, 0xe0, 0x0006}, {0xa0, 0x00, 0x0098}, {0xa0, 0x00, 0x009a}, {0xa0, 0x01, 0x009b}, {0xa0, 0xe8, 0x009c}, {0xa0, 0x02, 0x009d}, {0xa0, 0x88, 0x009e}, {0xa0, 0x00, 0x011a}, {0xa0, 0x00, 0x011c}, {0xa0, 0x05, 0x0012}, {0xa0, 0x01, 0x0092}, {0xa0, 0x0c, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x11, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x13, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x14, 0x0092}, {0xa0, 0x01, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x15, 0x0092}, {0xa0, 0xe8, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x16, 0x0092}, {0xa0, 0x02, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x17, 0x0092}, {0xa0, 0x88, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x00, 0x0019}, {0xa0, 0x0d, 0x0100}, {0xa0, 0x89, 0x018d}, {0xa0, 0x50, 0x01a8}, {0xa0, 0x00, 0x01ad}, {0xa0, 0xc0, 0x019b}, {0xa0, 0xa0, 0x019c}, {0xa0, 0x06, 0x0189}, {0xa0, 0x03, 0x01c5}, {0xa0, 0x13, 0x01cb}, {0xa0, 0x08, 0x0250}, {0xa0, 0x08, 0x0301}, {0xa1, 0x01, 0x0002}, {0xa0, 0x00, 0x0092}, {0xa0, 0x02, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa1, 0x01, 0x0095}, {0xa1, 0x01, 0x0096}, {0xa1, 0x01, 0x0008}, {0xa0, 0x03, 0x0008}, {0xa0, 0x08, 0x01c6}, {0xa1, 0x01, 0x01c8}, {0xa1, 0x01, 0x01c9}, {0xa1, 0x01, 0x01ca}, {0xa0, 0x0f, 0x01cb}, {0xa0, 0x60, 0x010a}, {0xa0, 0xf0, 0x010b}, {0xa0, 0xf0, 0x010c}, {0xa0, 0xf0, 0x010d}, {0xa0, 0x60, 0x010e}, {0xa0, 0xf0, 0x010f}, {0xa0, 0xf0, 0x0110}, {0xa0, 0xf0, 0x0111}, {0xa0, 0x60, 0x0112}, {0xa1, 0x01, 0x0180}, {0xa0, 0x10, 0x0180}, {0xa0, 0x00, 0x0019}, {0xa0, 0x25, 0x0092}, {0xa0, 0x07, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x26, 0x0092}, {0xa0, 0x53, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x27, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x10, 0x0190}, //2f {0xa0, 0x04, 0x0191}, //9b {0xa0, 0x60, 0x0192}, //80 {0xa0, 0x01, 0x0195}, {0xa0, 0xd4, 0x0196}, {0xa0, 0xc0, 0x0197}, {0xa0, 0x10, 0x018c}, {0xa0, 0x20, 0x018f}, {0xa0, 0x60, 0x01a8}, {0xa0, 0x10, 0x01a9}, {0xa0, 0x13, 0x01aa}, {0xa1, 0x01, 0x001d}, {0xa1, 0x01, 0x001e}, {0xa1, 0x01, 0x001f}, {0xa1, 0x01, 0x0020}, {0xa0, 0x40, 0x0180}, {0xa1, 0x01, 0x0180}, {0xa0, 0x42, 0x0180}, {0, 0, 0} }; static __u16 hv7131cxx_scale_data[][3] = { {0xa0, 0x01, 0x0000}, {0xa0, 0x00, 0x0002}, //diff {0xa0, 0x01, 0x0010}, {0xa0, 0x01, 0x0001}, {0xa0, 0x77, 0x0101}, {0xa0, 0x03, 0x0008}, {0xa0, 0x05, 0x0012}, {0xa0, 0x07, 0x0012}, {0xa0, 0x02, 0x0003}, {0xa0, 0x80, 0x0004}, {0xa0, 0x01, 0x0005}, {0xa0, 0xe0, 0x0006}, //1e0 {0xa0, 0x00, 0x0098}, {0xa0, 0x00, 0x009a}, {0xa0, 0x01, 0x009b}, {0xa0, 0xe8, 0x009c}, {0xa0, 0x02, 0x009d}, {0xa0, 0x88, 0x009e}, {0xa0, 0x00, 0x011a}, {0xa0, 0x00, 0x011c}, {0xa0, 0x05, 0x0012}, {0xa0, 0x01, 0x0092}, {0xa0, 0x0c, 0x0093}, //0c {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x11, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x13, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x14, 0x0092}, {0xa0, 0x01, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x15, 0x0092}, {0xa0, 0xe8, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x16, 0x0092}, {0xa0, 0x02, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x17, 0x0092}, {0xa0, 0x88, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x00, 0x0019}, //00 {0xa0, 0x0d, 0x0100}, {0xa0, 0x89, 0x018d}, {0xa0, 0x50, 0x01a8}, {0xa0, 0x00, 0x01ad}, {0xa0, 0xc0, 0x019b}, {0xa0, 0xa0, 0x019c}, {0xa0, 0x06, 0x0189}, {0xa0, 0x03, 0x01c5}, {0xa0, 0x13, 0x01cb}, {0xa0, 0x08, 0x0250}, {0xa0, 0x08, 0x0301}, {0xa1, 0x01, 0x0002}, {0xa0, 0x00, 0x0092}, //read the i2c chips ident {0xa0, 0x02, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa1, 0x01, 0x0095}, {0xa1, 0x01, 0x0096}, {0xa1, 0x01, 0x0008}, {0xa0, 0x03, 0x0008}, {0xa0, 0x08, 0x01c6}, {0xa1, 0x01, 0x01c8}, {0xa1, 0x01, 0x01c9}, {0xa1, 0x01, 0x01ca}, {0xa0, 0x0f, 0x01cb}, {0xa0, 0x60, 0x010a}, {0xa0, 0xf0, 0x010b}, {0xa0, 0xf0, 0x010c}, {0xa0, 0xf0, 0x010d}, {0xa0, 0x60, 0x010e}, {0xa0, 0xf0, 0x010f}, {0xa0, 0xf0, 0x0110}, {0xa0, 0xf0, 0x0111}, {0xa0, 0x60, 0x0112}, {0xa1, 0x01, 0x0180}, {0xa0, 0x10, 0x0180}, {0xa0, 0x00, 0x0019}, {0xa0, 0x25, 0x0092}, {0xa0, 0x07, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x26, 0x0092}, {0xa0, 0x53, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x27, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x10, 0x0190}, //2f {0xa0, 0x04, 0x0191}, //9b {0xa0, 0x60, 0x0192}, //80 {0xa0, 0x01, 0x0195}, {0xa0, 0xd4, 0x0196}, {0xa0, 0xc0, 0x0197}, {0xa0, 0x10, 0x018c}, {0xa0, 0x20, 0x018f}, {0xa0, 0x60, 0x01a8}, {0xa0, 0x10, 0x01a9}, {0xa0, 0x13, 0x01aa}, {0xa1, 0x01, 0x001d}, {0xa1, 0x01, 0x001e}, {0xa1, 0x01, 0x001f}, {0xa1, 0x01, 0x0020}, {0xa0, 0x40, 0x0180}, {0xa1, 0x01, 0x0180}, {0xa0, 0x42, 0x0180}, {0, 0, 0} }; pwcbsd/spca5xx-20060402/drivers/usb/icm105a.h000744 000423 000000 00000071653 10546676141 020672 0ustar00luigiwheel000000 000000 /**************************************************************************** # Ic-media ICM105A library # # Copyright (C) 2004 Michel Xhaard mxhaard@magic.fr # # # # This program is free software; you can redistribute it and/or modify # # it under the terms of the GNU General Public License as published by # # the Free Software Foundation; either version 2 of the License, or # # (at your option) any later version. # # # # This program is distributed in the hope that it will be useful, # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # # ****************************************************************************/ static __u16 icm105axx_start_data[][3] = { {0xa0, 0x01, 0x0000}, {0xa0, 0x10, 0x0002}, {0xa0, 0x03, 0x0008}, {0xa0, 0x0c, 0x0010}, {0xa0, 0x02, 0x0003}, {0xa0, 0x80, 0x0004}, {0xa0, 0x01, 0x0005}, {0xa0, 0xe0, 0x0006}, {0xa0, 0x01, 0x0001}, {0xa0, 0x03, 0x0012}, {0xa0, 0x01, 0x0012}, {0xa0, 0xa1, 0x008b}, {0xa0, 0x00, 0x0097}, {0xa0, 0x01, 0x0098}, {0xa0, 0x00, 0x0099}, {0xa0, 0x01, 0x009a}, {0xa0, 0x01, 0x011a}, {0xa0, 0x01, 0x011c}, {0xa0, 0x01, 0x009b}, {0xa0, 0xe8, 0x009c}, {0xa0, 0x02, 0x009d}, {0xa0, 0x88, 0x009e}, {0xa0, 0x37, 0x0101}, {0xa0, 0x0d, 0x0100}, {0xa0, 0x06, 0x0189}, {0xa0, 0x03, 0x01c5}, {0xa0, 0x13, 0x01cb}, {0xa0, 0x01, 0x0092}, {0xa0, 0x10, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x03, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x04, 0x0092}, {0xa0, 0x01, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x05, 0x0092}, {0xa0, 0x20, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x06, 0x0092}, {0xa0, 0x01, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x08, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x03, 0x0092}, {0xa0, 0x01, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x04, 0x0092}, {0xa0, 0x11, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x05, 0x0092}, {0xa0, 0xa0, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x06, 0x0092}, {0xa0, 0x01, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x08, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x03, 0x0092}, {0xa0, 0x02, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x04, 0x0092}, {0xa0, 0x13, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x05, 0x0092}, {0xa0, 0x20, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x06, 0x0092}, {0xa0, 0x01, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x08, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x03, 0x0092}, {0xa0, 0x03, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x04, 0x0092}, {0xa0, 0x15, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x05, 0x0092}, {0xa0, 0x20, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x06, 0x0092}, {0xa0, 0x05, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x08, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x03, 0x0092}, {0xa0, 0x04, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x04, 0x0092}, {0xa0, 0x17, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x05, 0x0092}, {0xa0, 0x20, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x06, 0x0092}, {0xa0, 0x0d, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x08, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x03, 0x0092}, {0xa0, 0x05, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x04, 0x0092}, {0xa0, 0x19, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x05, 0x0092}, {0xa0, 0x20, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x06, 0x0092}, {0xa0, 0x05, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x08, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x03, 0x0092}, {0xa0, 0x06, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x04, 0x0092}, {0xa0, 0x17, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x05, 0x0092}, {0xa0, 0x26, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x06, 0x0092}, {0xa0, 0x05, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x08, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x03, 0x0092}, {0xa0, 0x07, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x04, 0x0092}, {0xa0, 0x19, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x05, 0x0092}, {0xa0, 0x22, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x06, 0x0092}, {0xa0, 0x05, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x08, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x03, 0x0092}, {0xa0, 0x08, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x04, 0x0092}, {0xa0, 0x21, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x05, 0x0092}, {0xa0, 0xaa, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x06, 0x0092}, {0xa0, 0x05, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x08, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x03, 0x0092}, {0xa0, 0x09, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x04, 0x0092}, {0xa0, 0x23, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x05, 0x0092}, {0xa0, 0xaa, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x06, 0x0092}, {0xa0, 0x0d, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x08, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x03, 0x0092}, {0xa0, 0x0a, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x04, 0x0092}, {0xa0, 0x25, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x05, 0x0092}, {0xa0, 0xaa, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x06, 0x0092}, {0xa0, 0x05, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x08, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x03, 0x0092}, {0xa0, 0x0b, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x04, 0x0092}, {0xa0, 0xec, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x05, 0x0092}, {0xa0, 0x2e, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x06, 0x0092}, {0xa0, 0x05, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x08, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x03, 0x0092}, {0xa0, 0x0c, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x04, 0x0092}, {0xa0, 0xfa, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x05, 0x0092}, {0xa0, 0x2a, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x06, 0x0092}, {0xa0, 0x05, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x08, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x07, 0x0092}, {0xa0, 0x0d, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x01, 0x0092}, {0xa0, 0x05, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x94, 0x0092}, {0xa0, 0x02, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x90, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x91, 0x0092}, {0xa0, 0x1f, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x10, 0x0092}, {0xa0, 0x64, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x9b, 0x0092}, {0xa0, 0xf0, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x9c, 0x0092}, {0xa0, 0x02, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x14, 0x0092}, {0xa0, 0x1a, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x20, 0x0092}, {0xa0, 0x80, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x22, 0x0092}, {0xa0, 0x80, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x24, 0x0092}, {0xa0, 0x80, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x26, 0x0092}, {0xa0, 0x80, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x00, 0x0092}, {0xa0, 0x84, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x08, 0x0250}, {0xa0, 0x08, 0x0301}, {0xa0, 0xa8, 0x0092}, {0xa0, 0xc0, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa1, 0x01, 0x0002}, {0xa1, 0x01, 0x0008}, {0xa1, 0x01, 0x0180}, {0xa0, 0x02, 0x0180}, {0xa0, 0x40, 0x0116}, {0xa0, 0x40, 0x0117}, {0xa0, 0x40, 0x0118}, {0xa1, 0x01, 0x0008}, {0xa0, 0x03, 0x0008}, {0xa0, 0x08, 0x01c6}, {0xa1, 0x01, 0x01c8}, {0xa1, 0x01, 0x01c9}, {0xa1, 0x01, 0x01ca}, {0xa0, 0x0f, 0x01cb}, {0xa0, 0x52, 0x010a}, {0xa0, 0xf7, 0x010b}, {0xa0, 0xf7, 0x010c}, {0xa0, 0xf7, 0x010d}, {0xa0, 0x52, 0x010e}, {0xa0, 0xf7, 0x010f}, {0xa0, 0xf7, 0x0110}, {0xa0, 0xf7, 0x0111}, {0xa0, 0x52, 0x0112}, {0xa1, 0x01, 0x0180}, {0xa0, 0x00, 0x0180}, {0xa0, 0x00, 0x0019}, {0xa0, 0x0d, 0x0092}, {0xa0, 0x03, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x0c, 0x0092}, {0xa0, 0x8c, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x0e, 0x0092}, {0xa0, 0x95, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x0f, 0x0092}, {0xa0, 0x02, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x1c, 0x0092}, {0xa0, 0x94, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x1d, 0x0092}, {0xa0, 0x02, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x20, 0x0092}, {0xa0, 0x80, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x22, 0x0092}, {0xa0, 0x80, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x24, 0x0092}, {0xa0, 0x80, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x26, 0x0092}, {0xa0, 0x80, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x00, 0x0092}, {0xa0, 0x84, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x02, 0x00a3}, {0xa0, 0x94, 0x00a4}, {0xa0, 0x00, 0x0190}, {0xa0, 0x04, 0x0191}, {0xa0, 0x20, 0x0192}, {0xa0, 0x00, 0x0195}, {0xa0, 0x00, 0x0196}, {0xa0, 0x84, 0x0197}, {0xa0, 0x10, 0x018c}, {0xa0, 0x20, 0x018f}, {0xa0, 0x10, 0x01a9}, {0xa0, 0x12, 0x01aa}, {0xa0, 0xe3, 0x001d}, {0xa0, 0xec, 0x001e}, {0xa0, 0xf5, 0x001f}, {0xa0, 0xff, 0x0020}, {0xa0, 0x00, 0x01a7}, {0xa0, 0xc0, 0x01a8}, {0xa0, 0xc0, 0x011d}, {0xa0, 0x42, 0x0180}, {0xa1, 0x01, 0x0180}, {0xa0, 0x42, 0x0180}, {0xa0, 0x40, 0x0116}, {0xa0, 0x40, 0x0117}, {0xa0, 0x40, 0x0118}, {0, 0, 0}, }; static __u16 icm105axx_scale_data[][3] = { {0xa0, 0x01, 0x0000}, {0xa0, 0x00, 0x0002}, {0xa0, 0x03, 0x0008}, {0xa0, 0x0c, 0x0010}, {0xa0, 0x02, 0x0003}, {0xa0, 0x80, 0x0004}, {0xa0, 0x01, 0x0005}, {0xa0, 0xe0, 0x0006}, {0xa0, 0x01, 0x0001}, {0xa0, 0x03, 0x0012}, {0xa0, 0x01, 0x0012}, {0xa0, 0xa1, 0x008b}, {0xa0, 0x00, 0x0097}, {0xa0, 0x02, 0x0098}, {0xa0, 0x00, 0x0099}, {0xa0, 0x02, 0x009a}, {0xa0, 0x02, 0x011a}, {0xa0, 0x02, 0x011c}, {0xa0, 0x01, 0x009b}, {0xa0, 0xe6, 0x009c}, {0xa0, 0x02, 0x009d}, {0xa0, 0x86, 0x009e}, {0xa0, 0x77, 0x0101}, {0xa0, 0x0d, 0x0100}, {0xa0, 0x06, 0x0189}, {0xa0, 0x03, 0x01c5}, {0xa0, 0x13, 0x01cb}, {0xa0, 0x01, 0x0092}, {0xa0, 0x10, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x03, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x04, 0x0092}, {0xa0, 0x01, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x05, 0x0092}, {0xa0, 0x20, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x06, 0x0092}, {0xa0, 0x01, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x08, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x03, 0x0092}, {0xa0, 0x01, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x04, 0x0092}, {0xa0, 0x11, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x05, 0x0092}, {0xa0, 0xa0, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x06, 0x0092}, {0xa0, 0x01, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x08, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x03, 0x0092}, {0xa0, 0x02, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x04, 0x0092}, {0xa0, 0x13, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x05, 0x0092}, {0xa0, 0x20, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x06, 0x0092}, {0xa0, 0x01, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x08, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x03, 0x0092}, {0xa0, 0x03, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x04, 0x0092}, {0xa0, 0x15, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x05, 0x0092}, {0xa0, 0x20, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x06, 0x0092}, {0xa0, 0x05, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x08, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x03, 0x0092}, {0xa0, 0x04, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x04, 0x0092}, {0xa0, 0x17, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x05, 0x0092}, {0xa0, 0x20, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x06, 0x0092}, {0xa0, 0x0d, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x08, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x03, 0x0092}, {0xa0, 0x05, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x04, 0x0092}, {0xa0, 0x19, 0x0093}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x05, 0x0092}, {0xa0, 0x20, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x06, 0x0092}, {0xa0, 0x05, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x08, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x03, 0x0092}, {0xa0, 0x06, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x04, 0x0092}, {0xa0, 0x17, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x05, 0x0092}, {0xa0, 0x26, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x06, 0x0092}, {0xa0, 0x05, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x08, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x03, 0x0092}, {0xa0, 0x07, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x04, 0x0092}, {0xa0, 0x19, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x05, 0x0092}, {0xa0, 0x22, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x06, 0x0092}, {0xa0, 0x05, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x08, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x03, 0x0092}, {0xa0, 0x08, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x04, 0x0092}, {0xa0, 0x21, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x05, 0x0092}, {0xa0, 0xaa, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x06, 0x0092}, {0xa0, 0x05, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x08, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x03, 0x0092}, {0xa0, 0x09, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x04, 0x0092}, {0xa0, 0x23, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x05, 0x0092}, {0xa0, 0xaa, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x06, 0x0092}, {0xa0, 0x0d, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x08, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x03, 0x0092}, {0xa0, 0x0a, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x04, 0x0092}, {0xa0, 0x25, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x05, 0x0092}, {0xa0, 0xaa, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x06, 0x0092}, {0xa0, 0x05, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x08, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x03, 0x0092}, {0xa0, 0x0b, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x04, 0x0092}, {0xa0, 0xec, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x05, 0x0092}, {0xa0, 0x2e, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x06, 0x0092}, {0xa0, 0x05, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x08, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x03, 0x0092}, {0xa0, 0x0c, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x04, 0x0092}, {0xa0, 0xfa, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x05, 0x0092}, {0xa0, 0x2a, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x06, 0x0092}, {0xa0, 0x05, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x08, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x07, 0x0092}, {0xa0, 0x0d, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x01, 0x0092}, {0xa0, 0x05, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x94, 0x0092}, {0xa0, 0x02, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x90, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x91, 0x0092}, {0xa0, 0x10, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x10, 0x0092}, {0xa0, 0x64, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x9b, 0x0092}, {0xa0, 0xf0, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x9c, 0x0092}, {0xa0, 0x02, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x14, 0x0092}, {0xa0, 0x1a, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x20, 0x0092}, {0xa0, 0x80, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x22, 0x0092}, {0xa0, 0x80, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x24, 0x0092}, {0xa0, 0x80, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x26, 0x0092}, {0xa0, 0x80, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x00, 0x0092}, {0xa0, 0x84, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x08, 0x0250}, {0xa0, 0x08, 0x0301}, {0xa0, 0xa8, 0x0092}, {0xa0, 0x80, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x78, 0x018d}, {0xa1, 0x01, 0x0002}, {0xa1, 0x01, 0x0008}, {0xa1, 0x01, 0x0180}, {0xa0, 0x02, 0x0180}, {0xa0, 0x40, 0x0116}, {0xa0, 0x40, 0x0117}, {0xa0, 0x40, 0x0118}, {0xa1, 0x01, 0x0008}, {0xa0, 0x03, 0x0008}, {0xa0, 0x08, 0x01c6}, {0xa1, 0x01, 0x01c8}, {0xa1, 0x01, 0x01c9}, {0xa1, 0x01, 0x01ca}, {0xa0, 0x0f, 0x01cb}, {0xa0, 0x52, 0x010a}, {0xa0, 0xf7, 0x010b}, {0xa0, 0xf7, 0x010c}, {0xa0, 0xf7, 0x010d}, {0xa0, 0x52, 0x010e}, {0xa0, 0xf7, 0x010f}, {0xa0, 0xf7, 0x0110}, {0xa0, 0xf7, 0x0111}, {0xa0, 0x52, 0x0112}, {0xa1, 0x01, 0x0180}, {0xa0, 0x00, 0x0180}, {0xa0, 0x00, 0x0019}, {0xa0, 0x0d, 0x0092}, {0xa0, 0x03, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x0c, 0x0092}, {0xa0, 0x20, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x0e, 0x0092}, {0xa0, 0x0e, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x0f, 0x0092}, {0xa0, 0x02, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x1c, 0x0092}, {0xa0, 0x0d, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x1d, 0x0092}, {0xa0, 0x02, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x20, 0x0092}, {0xa0, 0x80, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x22, 0x0092}, {0xa0, 0x80, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x24, 0x0092}, {0xa0, 0x80, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x26, 0x0092}, {0xa0, 0x80, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x00, 0x0092}, {0xa0, 0x84, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x02, 0x00a3}, {0xa0, 0x0d, 0x00a4}, {0xa0, 0x00, 0x0190}, {0xa0, 0x04, 0x0191}, {0xa0, 0x1a, 0x0192}, {0xa0, 0x00, 0x0195}, {0xa0, 0x00, 0x0196}, {0xa0, 0x4b, 0x0197}, {0xa0, 0x10, 0x018c}, {0xa0, 0x20, 0x018f}, {0xa0, 0x10, 0x01a9}, {0xa0, 0x12, 0x01aa}, {0xa0, 0xc8, 0x001d}, {0xa0, 0xd8, 0x001e}, {0xa0, 0xea, 0x001f}, {0xa0, 0xff, 0x0020}, {0xa0, 0x00, 0x01a7}, {0xa0, 0x42, 0x0180}, {0xa1, 0x01, 0x0180}, {0xa0, 0x42, 0x0180}, {0xa0, 0x40, 0x0116}, {0xa0, 0x40, 0x0117}, {0xa0, 0x40, 0x0118}, {0, 0, 0} }; pwcbsd/spca5xx-20060402/drivers/usb/jpeg_header.h000744 000423 000000 00000045447 10546676141 021772 0ustar00luigiwheel000000 000000 #ifndef JPEG_HEADER_H #define JPEG_HEADER_H /* * Start from 0 -> 588 * 7 - 70 : Quantization Table[0] * 72 - 135 : Quantization Table[1] */ #define GSMART_JPG_DEFAULT_HEADER_PART1_LENGTH 136 #define GSMART_JPG_DEFAULT_HEADER_PART2_LENGTH 420 #define GSMART_JPG_DEFAULT_HEADER_PART3_LENGTH 33 #define GSMART_JPG_DEFAULT_HEADER_LENGTH 589 #define GSMART_JPG_HUFFMAN_TABLE_LENGTH 0x1A0 const unsigned char GsmartJPEGHuffmanTable[GSMART_JPG_HUFFMAN_TABLE_LENGTH] = { 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7D, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33, 0x52, 0xF0, 0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA }; const unsigned char GsmartJPEGScanTable[6] = { 0x01, 0x00, 0x02, 0x11, 0x03, 0x11 }; const unsigned char GsmartJPGDefaultHeaderPart1[GSMART_JPG_DEFAULT_HEADER_PART1_LENGTH] = { //SOI(Start of Image) 0xFF, 0xD8, //DQT(Define Quantization Table) 0xFF, 0xDB, 0x00, 0x84, 0x00, 0x05, 0x03, 0x04, 0x04, 0x04, 0x03, 0x05, 0x04, 0x04, 0x04, 0x06, 0x05, 0x05, 0x06, 0x08, 0x0D, 0x08, 0x08, 0x07, 0x07, 0x08, 0x10, 0x0C, 0x0C, 0x0A, 0x0D, 0x14, 0x11, 0x15, 0x14, 0x13, 0x11, 0x13, 0x13, 0x16, 0x18, 0x1F, 0x1A, 0x16, 0x17, 0x1E, 0x17, 0x13, 0x13, 0x1B, 0x25, 0x1C, 0x1E, 0x20, 0x21, 0x23, 0x23, 0x23, 0x15, 0x1A, 0x27, 0x29, 0x26, 0x22, 0x29, 0x1F, 0x22, 0x23, 0X22, 0X01, 0x05, 0x06, 0x06, 0x08, 0x07, 0x08, 0x10, 0x08, 0x08, 0x10, 0x22, 0x16, 0x13, 0x16, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22 }; const unsigned char GsmartJPGDefaultHeaderPart2[GSMART_JPG_DEFAULT_HEADER_PART2_LENGTH] = { //DHT(Define Huffman Table) 0xFF, 0xC4, 0x01, 0xA2, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7D, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33, 0x52, 0xF0, 0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA }; /* * 5 - 6 : Image Height(H Byte,L Byte) * 7 - 8 : Image Width(H Byte,L Byte) */ const unsigned char GsmartJPGDefaultHeaderPart3[GSMART_JPG_DEFAULT_HEADER_PART3_LENGTH] = { //SOFn(Start of Frame) 0xFF, 0xC0, 0x00, 0x11, 0x08, 0x00, 0x78, 0x00, 0xA0, 0x03, 0x01, 0x21, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, //SOS(Start of Scan) 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3F, 0x00 }; const unsigned char GsmartQTable[22][64] = { // index 0, Q50 {16, 11, 12, 14, 12, 10, 16, 14, 13, 14, 18, 17, 16, 19, 24, 40, 26, 24, 22, 22, 24, 49, 35, 37, 29, 40, 58, 51, 61, 60, 57, 51, 56, 55, 64, 72, 92, 78, 64, 68, 87, 69, 55, 56, 80, 109, 81, 87, 95, 98, 103, 104, 103, 62, 77, 113, 121, 112, 100, 120, 92, 101, 103, 99}, {17, 18, 18, 24, 21, 24, 47, 26, 26, 47, 99, 66, 56, 66, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}, // index 1, Q70 {10, 7, 7, 8, 7, 6, 10, 8, 8, 8, 11, 10, 10, 11, 14, 24, 16, 14, 13, 13, 14, 29, 21, 22, 17, 24, 35, 31, 37, 36, 34, 31, 34, 33, 38, 43, 55, 47, 38, 41, 52, 41, 33, 34, 48, 65, 49, 52, 57, 59, 62, 62, 62, 37, 46, 68, 73, 67, 60, 72, 55, 61, 62, 59}, {10, 11, 11, 14, 13, 14, 28, 16, 16, 28, 59, 40, 34, 40, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59}, // index 2, Q80 {6, 4, 5, 6, 5, 4, 6, 6, 5, 6, 7, 7, 6, 8, 10, 16, 10, 10, 9, 9, 10, 20, 14, 15, 12, 16, 23, 20, 24, 24, 23, 20, 22, 22, 26, 29, 37, 31, 26, 27, 35, 28, 22, 22, 32, 44, 32, 35, 38, 39, 41, 42, 41, 25, 31, 45, 48, 45, 40, 48, 37, 40, 41, 40}, {7, 7, 7, 10, 8, 10, 19, 10, 10, 19, 40, 26, 22, 26, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40}, // index 3, Q85 {5, 3, 4, 4, 4, 3, 5, 4, 4, 4, 5, 5, 5, 6, 7, 12, 8, 7, 7, 7, 7, 15, 11, 11, 9, 12, 17, 15, 18, 18, 17, 15, 17, 17, 19, 22, 28, 23, 19, 20, 26, 21, 17, 17, 24, 33, 24, 26, 29, 29, 31, 31, 31, 19, 23, 34, 36, 34, 30, 36, 28, 30, 31, 30}, {5, 5, 5, 7, 6, 7, 14, 8, 8, 14, 30, 20, 17, 20, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // index 4, Q90 {3, 2, 2, 3, 2, 2, 3, 3, 3, 3, 4, 3, 3, 4, 5, 8, 5, 5, 4, 4, 5, 10, 7, 7, 6, 8, 12, 10, 12, 12, 11, 10, 11, 11, 13, 14, 18, 16, 13, 14, 17, 14, 11, 11, 16, 22, 16, 17, 19, 20, 21, 21, 21, 12, 15, 23, 24, 22, 20, 24, 18, 20, 21, 20}, {3, 4, 4, 5, 4, 5, 9, 5, 5, 9, 20, 13, 11, 13, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20}, // index 5, Q60 {13, 9, 10, 11, 10, 8, 13, 11, 10, 11, 14, 14, 13, 15, 19, 32, 21, 19, 18, 18, 19, 39, 28, 30, 23, 32, 46, 41, 49, 48, 46, 41, 45, 44, 51, 58, 74, 62, 51, 54, 70, 55, 44, 45, 64, 87, 65, 70, 76, 78, 82, 83, 82, 50, 62, 90, 97, 90, 80, 96, 74, 81, 82, 79}, {14, 14, 14, 19, 17, 19, 38, 21, 21, 38, 79, 53, 45, 53, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79}, // index 6, Q25 {32, 22, 24, 28, 24, 20, 32, 28, 26, 28, 36, 34, 32, 38, 48, 80, 52, 48, 44, 44, 48, 98, 70, 74, 58, 80, 116, 102, 122, 120, 114, 102, 112, 110, 128, 144, 184, 156, 128, 136, 174, 138, 110, 112, 160, 218, 162, 174, 190, 196, 206, 208, 206, 124, 154, 226, 242, 224, 200, 240, 184, 202, 206, 198}, {34, 36, 36, 48, 42, 48, 94, 52, 52, 94, 198, 132, 112, 132, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198}, // index 7, Q95 {2, 1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 2, 2, 2, 2, 4, 3, 2, 2, 2, 2, 5, 4, 4, 3, 4, 6, 5, 6, 6, 6, 5, 6, 6, 6, 7, 9, 8, 6, 7, 9, 7, 6, 6, 8, 11, 8, 9, 10, 10, 10, 10, 10, 6, 8, 11, 12, 11, 10, 12, 9, 10, 10, 10}, {2, 2, 2, 2, 2, 2, 5, 3, 3, 5, 10, 7, 6, 7, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, // index 8, Q93 {2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 3, 2, 2, 3, 3, 6, 4, 3, 3, 3, 3, 7, 5, 5, 4, 6, 8, 7, 9, 8, 8, 7, 8, 8, 9, 10, 13, 11, 9, 10, 12, 10, 8, 8, 11, 15, 11, 12, 13, 14, 14, 15, 14, 9, 11, 16, 17, 16, 14, 17, 13, 14, 14, 14}, {2, 3, 3, 3, 3, 3, 7, 4, 4, 7, 14, 9, 8, 9, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14}, // index 9, Q40 {20, 14, 15, 18, 15, 13, 20, 18, 16, 18, 23, 21, 20, 24, 30, 50, 33, 30, 28, 28, 30, 61, 44, 46, 36, 50, 73, 64, 76, 75, 71, 64, 70, 69, 80, 90, 115, 98, 80, 85, 109, 86, 69, 70, 100, 136, 101, 109, 119, 123, 129, 130, 129, 78, 96, 141, 151, 140, 125, 150, 115, 126, 129, 124}, {21, 23, 23, 30, 26, 30, 59, 33, 33, 59, 124, 83, 70, 83, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124}, // index 10, pccam300 {5, 3, 3, 5, 7, 12, 15, 18, 4, 4, 4, 6, 8, 17, 18, 17, 4, 4, 5, 7, 12, 17, 21, 17, 4, 5, 7, 9, 15, 26, 24, 19, 5, 7, 11, 17, 20, 33, 31, 23, 7, 11, 17, 19, 24, 31, 34, 28, 15, 19, 23, 26, 31, 36, 36, 30, 22, 28, 29, 29, 34, 30, 31, 30}, {5, 5, 7, 14, 30, 30, 30, 30, 5, 6, 8, 20, 30, 30, 30, 30, 7, 8, 17, 30, 30, 30, 30, 30, 14, 20, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30} }; const unsigned char GsmartOriginal_QTable_4NF_M[18][64] = //not Z-Z { // index 0, Q50 {16, 11, 10, 16, 24, 40, 51, 61, 12, 12, 14, 19, 26, 58, 60, 55, 14, 13, 16, 24, 40, 57, 69, 56, 14, 17, 22, 29, 51, 87, 80, 62, 18, 22, 37, 56, 68, 109, 103, 77, 24, 35, 55, 64, 81, 104, 113, 92, 49, 64, 78, 87, 103, 121, 120, 101, 72, 92, 95, 98, 112, 100, 103, 99}, {17, 18, 24, 47, 99, 99, 99, 99, 18, 21, 26, 66, 99, 99, 99, 99, 24, 26, 56, 99, 99, 99, 99, 99, 47, 66, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}, // index 1, Q70 {10, 7, 6, 10, 14, 24, 31, 37, 7, 7, 8, 11, 16, 35, 36, 33, 8, 8, 10, 14, 24, 34, 41, 34, 8, 10, 13, 17, 31, 52, 48, 37, 11, 13, 22, 34, 41, 65, 62, 46, 14, 21, 33, 38, 49, 62, 68, 55, 29, 38, 47, 52, 62, 73, 72, 61, 43, 55, 57, 59, 67, 60, 62, 59}, {10, 11, 14, 28, 59, 59, 59, 59, 11, 13, 16, 40, 59, 59, 59, 59, 14, 16, 34, 59, 59, 59, 59, 59, 28, 40, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59}, // index 2, Q80 {6, 4, 4, 6, 10, 16, 20, 24, 5, 5, 6, 8, 10, 23, 24, 22, 6, 5, 6, 10, 16, 23, 28, 22, 6, 7, 9, 12, 20, 35, 32, 25, 7, 9, 15, 22, 27, 44, 41, 31, 10, 14, 22, 26, 32, 42, 45, 37, 20, 26, 31, 35, 41, 48, 48, 40, 29, 37, 38, 39, 45, 40, 41, 40}, {7, 7, 10, 19, 40, 40, 40, 40, 7, 8, 10, 26, 40, 40, 40, 40, 10, 10, 22, 40, 40, 40, 40, 40, 19, 26, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40}, // index 3, Q85 {5, 3, 3, 5, 7, 12, 15, 18, 4, 4, 4, 6, 8, 17, 18, 17, 4, 4, 5, 7, 12, 17, 21, 17, 4, 5, 7, 9, 15, 26, 24, 19, 5, 7, 11, 17, 20, 33, 31, 23, 7, 11, 17, 19, 24, 31, 34, 28, 15, 19, 23, 26, 31, 36, 36, 30, 22, 28, 29, 29, 34, 30, 31, 30}, {5, 5, 7, 14, 30, 30, 30, 30, 5, 6, 8, 20, 30, 30, 30, 30, 7, 8, 17, 30, 30, 30, 30, 30, 14, 20, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // index 4, Q90 {3, 2, 2, 3, 5, 8, 10, 12, 2, 2, 3, 4, 5, 12, 12, 11, 3, 3, 3, 5, 8, 11, 14, 11, 3, 3, 4, 6, 10, 17, 16, 12, 4, 4, 7, 11, 14, 22, 21, 15, 5, 7, 11, 13, 16, 21, 23, 18, 10, 13, 16, 17, 21, 24, 24, 20, 14, 18, 19, 20, 22, 20, 21, 20}, {3, 4, 5, 9, 20, 20, 20, 20, 4, 4, 5, 13, 20, 20, 20, 20, 5, 5, 11, 20, 20, 20, 20, 20, 9, 13, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20}, // index 5, Q60 {13, 9, 8, 13, 19, 32, 41, 49, 10, 10, 11, 15, 21, 46, 48, 44, 11, 10, 13, 19, 32, 46, 55, 45, 11, 14, 18, 23, 41, 70, 64, 50, 14, 18, 30, 45, 54, 87, 82, 62, 19, 28, 44, 51, 65, 83, 90, 74, 39, 51, 62, 70, 82, 97, 96, 81, 58, 74, 76, 78, 90, 80, 82, 79}, {14, 14, 19, 38, 79, 79, 79, 79, 14, 17, 21, 53, 79, 79, 79, 79, 19, 21, 45, 79, 79, 79, 79, 79, 38, 53, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79}, // index 6, Q25 {32, 22, 20, 32, 48, 80, 102, 122, 24, 24, 28, 38, 52, 116, 120, 110, 28, 26, 32, 48, 80, 114, 138, 112, 28, 34, 44, 58, 102, 174, 160, 124, 36, 44, 74, 112, 136, 218, 206, 154, 48, 70, 110, 128, 162, 208, 226, 184, 98, 128, 156, 174, 206, 242, 240, 202, 144, 184, 190, 196, 224, 200, 206, 198}, {34, 36, 48, 94, 198, 198, 198, 198, 36, 42, 52, 132, 198, 198, 198, 198, 48, 52, 112, 198, 198, 198, 198, 198, 94, 132, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198}, // index 7, Q95 {2, 1, 1, 2, 2, 4, 5, 6, 1, 1, 1, 2, 3, 6, 6, 6, 1, 1, 2, 2, 4, 6, 7, 6, 1, 2, 2, 3, 5, 9, 8, 6, 2, 2, 4, 6, 7, 11, 10, 8, 2, 4, 6, 6, 8, 10, 11, 9, 5, 6, 8, 9, 10, 12, 12, 10, 7, 9, 10, 10, 11, 10, 10, 10}, {2, 2, 2, 5, 10, 10, 10, 10, 2, 2, 3, 7, 10, 10, 10, 10, 2, 3, 6, 10, 10, 10, 10, 10, 5, 7, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, // index 8, Q93 {2, 2, 1, 2, 3, 6, 7, 9, 2, 2, 2, 3, 4, 8, 8, 8, 2, 2, 2, 3, 6, 8, 10, 8, 2, 2, 3, 4, 7, 12, 11, 9, 3, 3, 5, 8, 10, 15, 14, 11, 3, 5, 8, 9, 11, 15, 16, 13, 7, 9, 11, 12, 14, 17, 17, 14, 10, 13, 13, 14, 16, 14, 14, 14}, {2, 3, 3, 7, 14, 14, 14, 14, 3, 3, 4, 9, 14, 14, 14, 14, 3, 4, 8, 14, 14, 14, 14, 14, 7, 9, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14} }; #endif /* JPEG_HEADER_H */ pwcbsd/spca5xx-20060402/drivers/usb/jpeg_qtables.h000744 000423 000000 00000010203 10546676141 022153 0ustar00luigiwheel000000 000000 /* * JPEG Q-tables. */ #ifndef JPEG_QTABLES_H #define JPEG_QTABLES_H static unsigned char qtable_creative_pccam[2][64] = { { /* Q-table Y-components */ 0x05, 0x03, 0x03, 0x05, 0x07, 0x0c, 0x0f, 0x12, 0x04, 0x04, 0x04, 0x06, 0x08, 0x11, 0x12, 0x11, 0x04, 0x04, 0x05, 0x07, 0x0c, 0x11, 0x15, 0x11, 0x04, 0x05, 0x07, 0x09, 0x0f, 0x1a, 0x18, 0x13, 0x05, 0x07, 0x0b, 0x11, 0x14, 0x21, 0x1f, 0x17, 0x07, 0x0b, 0x11, 0x13, 0x18, 0x1f, 0x22, 0x1c, 0x0f, 0x13, 0x17, 0x1a, 0x1f, 0x24, 0x24, 0x1e, 0x16, 0x1c, 0x1d, 0x1d, 0x22, 0x1e, 0x1f, 0x1e}, { /* Q-table C-components */ 0x05, 0x05, 0x07, 0x0e, 0x1e, 0x1e, 0x1e, 0x1e, 0x05, 0x06, 0x08, 0x14, 0x1e, 0x1e, 0x1e, 0x1e, 0x07, 0x08, 0x11, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x0e, 0x14, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e} }; static unsigned char qtable_kodak_ez200[2][64] = { { /* Q-table Y-components */ 0x02, 0x01, 0x01, 0x02, 0x02, 0x04, 0x05, 0x06, 0x01, 0x01, 0x01, 0x02, 0x03, 0x06, 0x06, 0x06, 0x01, 0x01, 0x02, 0x02, 0x04, 0x06, 0x07, 0x06, 0x01, 0x02, 0x02, 0x03, 0x05, 0x09, 0x08, 0x06, 0x02, 0x02, 0x04, 0x06, 0x07, 0x0b, 0x0a, 0x08, 0x02, 0x04, 0x06, 0x06, 0x08, 0x0a, 0x0b, 0x09, 0x05, 0x06, 0x08, 0x09, 0x0a, 0x0c, 0x0c, 0x0a, 0x07, 0x09, 0x0a, 0x0a, 0x0b, 0x0a, 0x0a, 0x0a}, { /* Q-table C-components */ 0x02, 0x02, 0x02, 0x05, 0x0a, 0x0a, 0x0a, 0x0a, 0x02, 0x02, 0x03, 0x07, 0x0a, 0x0a, 0x0a, 0x0a, 0x02, 0x03, 0x06, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x05, 0x07, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a} }; /* FIXME: This Q-table is identical to the Creative PC-CAM one, * except for one byte. Possibly a typo? * NWG: 18/05/2003. */ static unsigned char qtable_spca504_default[2][64] = { { /* Q-table Y-components */ 0x05, 0x03, 0x03, 0x05, 0x07, 0x0c, 0x0f, 0x12, 0x04, 0x04, 0x04, 0x06, 0x08, 0x11, 0x12, 0x11, 0x04, 0x04, 0x05, 0x07, 0x0c, 0x11, 0x15, 0x11, 0x04, 0x05, 0x07, 0x09, 0x0f, 0x1a, 0x18, 0x13, 0x05, 0x07, 0x0b, 0x11, 0x14, 0x21, 0x1f, 0x17, 0x07, 0x0b, 0x11, 0x13, 0x18, 0x1f, 0x22, 0x1c, 0x0f, 0x13, 0x17, 0x1a, 0x1f, 0x24, 0x24, 0x1e, 0x16, 0x1c, 0x1d, 0x1d, 0x1d /* 0x22 */ , 0x1e, 0x1f, 0x1e, }, { /* Q-table C-components */ 0x05, 0x05, 0x07, 0x0e, 0x1e, 0x1e, 0x1e, 0x1e, 0x05, 0x06, 0x08, 0x14, 0x1e, 0x1e, 0x1e, 0x1e, 0x07, 0x08, 0x11, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x0e, 0x14, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e} }; static unsigned char qtable_pocketdv[2][64] = { { /* Q-table Y-components start registers 0x8800 */ 0x06, 0x04, 0x04, 0x06, 0x0a, 0x10, 0x14, 0x18, 0x05, 0x05, 0x06, 0x08, 0x0a, 0x17, 0x18, 0x16, 0x06, 0x05, 0x06, 0x0a, 0x10, 0x17, 0x1c, 0x16, 0x06, 0x07, 0x09, 0x0c, 0x14, 0x23, 0x20, 0x19, 0x07, 0x09, 0x0f, 0x16, 0x1b, 0x2c, 0x29, 0x1f, 0x0a, 0x0e, 0x16, 0x1a, 0x20, 0x2a, 0x2d, 0x25, 0x14, 0x1a, 0x1f, 0x23, 0x29, 0x30, 0x30, 0x28, 0x1d, 0x25, 0x26, 0x27, 0x2d, 0x28, 0x29, 0x28, }, { /* Q-table C-components start registers 0x8840 */ 0x07, 0x07, 0x0a, 0x13, 0x28, 0x28, 0x28, 0x28, 0x07, 0x08, 0x0a, 0x1a, 0x28, 0x28, 0x28, 0x28, 0x0a, 0x0a, 0x16, 0x28, 0x28, 0x28, 0x28, 0x28, 0x13, 0x1a, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28} }; #endif /* JPEG_QTABLES_H */ pwcbsd/spca5xx-20060402/drivers/usb/mr97311.h000755 000423 000000 00000031563 10552537117 020550 0ustar00luigiwheel000000 000000 /**************************************************************************** # Mars-Semi MR97311A library # # Copyright (C) 2005 # # Part of spca5xx project # # This program is free software; you can redistribute it and/or modify # # it under the terms of the GNU General Public License as published by # # the Free Software Foundation; either version 2 of the License, or # # (at your option) any later version. # # # # This program is distributed in the hope that it will be useful, # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # # ****************************************************************************/ static int pcam_start(struct usb_spca50x *pcam); static inline void pcam_stop(struct usb_spca50x *pcam); static int mr97311_config(struct usb_spca50x *pcam); static int pcam_reg_write(struct usb_device *dev, __u16 index, unsigned char *value, int length); static void MISensor_BulkWrite(struct usb_device *dev, unsigned short *pch, char Address, int length, char controlbyte); //MI Register table //elvis enum { REG_HW_MI_0, REG_HW_MI_1, REG_HW_MI_2, REG_HW_MI_3, REG_HW_MI_4, REG_HW_MI_5, REG_HW_MI_6, REG_HW_MI_7, REG_HW_MI_9 = 0x09, REG_HW_MI_B = 0x0B, REG_HW_MI_C, REG_HW_MI_D, REG_HW_MI_1E = 0x1E, REG_HW_MI_20 = 0x20, REG_HW_MI_2B = 0x2B, REG_HW_MI_2C, REG_HW_MI_2D, REG_HW_MI_2E, REG_HW_MI_35 = 0x35, REG_HW_MI_5F = 0x5F, REG_HW_MI_60, REG_HW_MI_61, REG_HW_MI_62, REG_HW_MI_63, REG_HW_MI_64, REG_HW_MI_F1 = 0xF1, ATTR_TOTAL_MI_REG = 242 }; static inline void pcam_stop(struct usb_spca50x *pcam) { int result; char data[2]; memset(data, 0, 2); data[0] = 1; data[1] = 0; result = pcam_reg_write(pcam->dev, data[0], data, 2); if (result < 0) printk("Camera Stop failed \n"); } static int pcam_reg_write(struct usb_device *dev, __u16 index, unsigned char *value, int length) { unsigned char buf[12]; int rc; int i; unsigned char index_value = 0; memset(buf, 0, sizeof(buf)); for (i = 0; i < length; i++) buf[i] = value[i]; rc = usb_control_msg(dev, usb_sndbulkpipe(dev, 4), 0x12, 0xc8, index_value, index, value, length, 5 * HZ); PDEBUG(1, "reg write: 0x%02X , result = 0x%x \n", index, rc); if (rc < 0) { PDEBUG(1, "reg write: error %d \n", rc); } return rc; } static int pcam_start(struct usb_spca50x *pcam) { int err_code; unsigned char data[242]; unsigned short MI_buf[242]; int h_size, v_size; int intpipe; //struct usb_device *dev = pcam->dev; memset(data, 0, 242); memset(MI_buf, 0, 242); PDEBUG(1, "usb_set_interface in pcamCameraStart , interface %d , alt 8 \n", pcam->iface); #if !defined(__FreeBSD__) /* XXX must be fixed */ if (usb_set_interface(pcam->dev, pcam->iface, 8) < 0) { err("Set packet size: set interface error"); return -EBUSY; } #endif data[0] = 0x01; //address data[1] = 0x01; err_code = pcam_reg_write(pcam->dev, data[0], data, 0x02); if (err_code < 0) { printk("Register write failed \n"); return -1; } /* Initialize the MR97113 chip register */ data[0] = 0x00; //address data[1] = 0x0c | 0x01; //reg 0 data[2] = 0x01; //reg 1 switch (pcam->width) { case 1280: h_size = 1280; v_size = 1024; break; case 640: h_size = 640; v_size = 480; break; case 384: h_size = 384; v_size = 288; break; case 352: h_size = 352; v_size = 288; break; case 320: h_size = 320; v_size = 240; break; default: h_size = 352; v_size = 288; break; } data[3] = h_size / 8; //h_size , reg 2 data[4] = v_size / 8; //v_size , reg 3 data[5] = 0x30; // reg 4, MI, PAS5101 : 0x30 for 24mhz , 0x28 for 12mhz data[6] = 4; // reg 5, H start data[7] = 0xc0; // reg 6, gamma 1.5 data[8] = 3; // reg 7, V start //if(pcam->width == 320 ) //data[9]= 0x56; // reg 8, 24MHz, 2:1 scale down //else data[9] = 0x52; // reg 8, 24MHz, no scale down data[10] = 0x5d; // reg 9, I2C device address [for PAS5101 (0x40)] [for MI (0x5d)] err_code = pcam_reg_write(pcam->dev, data[0], data, 0x0b); if (err_code < 0) { PDEBUG(1, "Register write failed \n"); return -1; } data[0] = 0x23; //address data[1] = 0x09; // reg 35, append frame header err_code = pcam_reg_write(pcam->dev, data[0], data, 0x02); if (err_code < 0) { PDEBUG(1, "Register write failed \n"); return -1; } data[0] = 0x3C; //address if (pcam->width == 1280) data[1] = 200; // reg 60, pc-cam frame size (unit: 4KB) 800KB else data[1] = 50; // 50 reg 60, pc-cam frame size (unit: 4KB) 200KB err_code = pcam_reg_write(pcam->dev, data[0], data, 0x02); if (err_code < 0) { PDEBUG(1, "Register write failed \n"); return -1; } if (0) { // fixed dark-gain data[1] = 0; // reg 94, Y Gain (1.75) data[2] = 0; // reg 95, UV Gain (1.75) data[3] = 0x3f; // reg 96, Y Gain/UV Gain/disable auto dark-gain data[4] = 0; // reg 97, set fixed dark level data[5] = 0; // reg 98, don't care } else { // auto dark-gain data[1] = 0; // reg 94, Y Gain (auto) data[2] = 0; // reg 95, UV Gain (1.75) data[3] = 0x78; // reg 96, Y Gain/UV Gain/disable auto dark-gain switch (pcam->width) { case 1280: data[4] = 154; // reg 97, %3 shadow point (unit: 256 pixel) data[5] = 51; // reg 98, %1 highlight point (uint: 256 pixel) break; case 640: data[4] = 36; // reg 97, %3 shadow point (unit: 256 pixel) data[5] = 12; // reg 98, %1 highlight point (uint: 256 pixel) break; case 320: data[4] = 9; // reg 97, %3 shadow point (unit: 256 pixel) data[5] = 3; // reg 98, %1 highlight point (uint: 256 pixel) break; } } // auto dark-gain data[0] = 0x5E; // address err_code = pcam_reg_write(pcam->dev, data[0], data, 0x06); if (err_code < 0) { PDEBUG(1, "Register write failed \n"); return -1; } data[0] = 0x67; data[1] = 0x13; // reg 103, first pixel B, disable sharpness err_code = pcam_reg_write(pcam->dev, data[0], data, 0x02); if (err_code < 0) { PDEBUG(1, "Register write failed \n"); return -1; } /* initialize the value of MI sensor... */ MI_buf[REG_HW_MI_1] = 0x000a; MI_buf[REG_HW_MI_2] = 0x000c; MI_buf[REG_HW_MI_3] = 0x0405; MI_buf[REG_HW_MI_4] = 0x0507; //mi_Attr_Reg_[REG_HW_MI_5] = 0x01ff;//13 MI_buf[REG_HW_MI_5] = 0x0013; //13 MI_buf[REG_HW_MI_6] = 0x001f; // vertical blanking //mi_Attr_Reg_[REG_HW_MI_6] = 0x0400; // vertical blanking MI_buf[REG_HW_MI_7] = 0x0002; //mi_Attr_Reg_[REG_HW_MI_9] = 0x015f; //mi_Attr_Reg_[REG_HW_MI_9] = 0x030f; MI_buf[REG_HW_MI_9] = 0x0374; MI_buf[REG_HW_MI_B] = 0x0000; MI_buf[REG_HW_MI_C] = 0x0000; MI_buf[REG_HW_MI_D] = 0x0000; MI_buf[REG_HW_MI_1E] = 0x8000; // mi_Attr_Reg_[REG_HW_MI_20] = 0x1104; MI_buf[REG_HW_MI_20] = 0x1104; //0x111c; MI_buf[REG_HW_MI_2B] = 0x0008; // mi_Attr_Reg_[REG_HW_MI_2C] = 0x000f; MI_buf[REG_HW_MI_2C] = 0x001f; //lita suggest MI_buf[REG_HW_MI_2D] = 0x0008; MI_buf[REG_HW_MI_2E] = 0x0008; MI_buf[REG_HW_MI_35] = 0x0051; MI_buf[REG_HW_MI_5F] = 0x0904; //fail to write MI_buf[REG_HW_MI_60] = 0x0000; MI_buf[REG_HW_MI_61] = 0x0000; MI_buf[REG_HW_MI_62] = 0x0498; MI_buf[REG_HW_MI_63] = 0x0000; MI_buf[REG_HW_MI_64] = 0x0000; MI_buf[REG_HW_MI_F1] = 0x0001; //changing while setting up the different value of dx/dy if (pcam->width != 1280) { MI_buf[0x01] = 0x010a; MI_buf[0x02] = 0x014c; MI_buf[0x03] = 0x01e5; MI_buf[0x04] = 0x0287; } MI_buf[0x20] = 0x1104; MISensor_BulkWrite(pcam->dev, MI_buf + 1, 1, 1, 0); MISensor_BulkWrite(pcam->dev, MI_buf + 2, 2, 1, 0); MISensor_BulkWrite(pcam->dev, MI_buf + 3, 3, 1, 0); MISensor_BulkWrite(pcam->dev, MI_buf + 4, 4, 1, 0); MISensor_BulkWrite(pcam->dev, MI_buf + 5, 5, 1, 0); MISensor_BulkWrite(pcam->dev, MI_buf + 6, 6, 1, 0); MISensor_BulkWrite(pcam->dev, MI_buf + 7, 7, 1, 0); MISensor_BulkWrite(pcam->dev, MI_buf + 9, 9, 1, 0); MISensor_BulkWrite(pcam->dev, MI_buf + 0x0B, 0x0B, 1, 0); MISensor_BulkWrite(pcam->dev, MI_buf + 0x0C, 0x0C, 1, 0); MISensor_BulkWrite(pcam->dev, MI_buf + 0x0D, 0x0D, 1, 0); MISensor_BulkWrite(pcam->dev, MI_buf + 0x1E, 0x1E, 1, 0); MISensor_BulkWrite(pcam->dev, MI_buf + 0x20, 0x20, 1, 0); MISensor_BulkWrite(pcam->dev, MI_buf + 0x2B, 0x2B, 1, 0); MISensor_BulkWrite(pcam->dev, MI_buf + 0x2C, 0x2C, 1, 0); MISensor_BulkWrite(pcam->dev, MI_buf + 0x2D, 0x2D, 1, 0); MISensor_BulkWrite(pcam->dev, MI_buf + 0x2E, 0x2E, 1, 0); MISensor_BulkWrite(pcam->dev, MI_buf + 0x35, 0x35, 1, 0); MISensor_BulkWrite(pcam->dev, MI_buf + 0x5F, 0x5F, 1, 0); MISensor_BulkWrite(pcam->dev, MI_buf + 0x60, 0x60, 1, 0); MISensor_BulkWrite(pcam->dev, MI_buf + 0x61, 0x61, 1, 0); MISensor_BulkWrite(pcam->dev, MI_buf + 0x62, 0x62, 1, 0); MISensor_BulkWrite(pcam->dev, MI_buf + 0x63, 0x63, 1, 0); MISensor_BulkWrite(pcam->dev, MI_buf + 0x64, 0x64, 1, 0); MISensor_BulkWrite(pcam->dev, MI_buf + 0xF1, 0xF1, 1, 0); intpipe = usb_sndintpipe(pcam->dev, 0); #if !defined(__FreeBSD__) /* XXX must be fixed */ err_code = usb_clear_halt(pcam->dev, intpipe); #endif data[0] = 0x00; data[1] = 0x4D; // ISOC transfering enable... err_code = pcam_reg_write(pcam->dev, data[0], data, 0x02); if (err_code < 0) { PDEBUG(1, "Register write failed \n"); return -1; } return 0; } static void MISensor_BulkWrite(struct usb_device *dev, unsigned short *pch, char Address, int length, char controlbyte) { int dest, src, result; unsigned char data[6]; memset(data, 0, 6); for (dest = 3, src = 0; src < length; src++) { data[0] = 0x1f; data[1] = controlbyte; data[2] = Address + src; data[dest] = pch[src] >> 8; //high byte; data[dest + 1] = pch[src]; //low byte; data[dest + 2] = 0; result = usb_control_msg(dev, usb_sndbulkpipe(dev, 4), 0x12, 0xc8, 0, Address, data, 5, 5 * HZ); PDEBUG(1, "reg write: 0x%02X , result = 0x%x \n", Address, result); if (result < 0) { printk("reg write: error %d \n", result); } } } static int mr97311_config(struct usb_spca50x *spca50x) { memset(spca50x->mode_cam, 0x00, TOTMODE * sizeof(struct mwebcam)); spca50x->mode_cam[VGA].width = 640; spca50x->mode_cam[VGA].height = 480; spca50x->mode_cam[VGA].t_palette = P_JPEG | P_RAW | P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[VGA].pipe = 1023; spca50x->mode_cam[VGA].method = 0; spca50x->mode_cam[VGA].mode = 1; spca50x->mode_cam[PAL].width = 384; spca50x->mode_cam[PAL].height = 288; spca50x->mode_cam[PAL].t_palette = P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[PAL].pipe = 1023; spca50x->mode_cam[PAL].method = 1; spca50x->mode_cam[PAL].mode = 1; spca50x->mode_cam[SIF].width = 352; spca50x->mode_cam[SIF].height = 288; spca50x->mode_cam[SIF].t_palette = P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[SIF].pipe = 1023; spca50x->mode_cam[SIF].method = 1; spca50x->mode_cam[SIF].mode = 1; spca50x->mode_cam[CIF].width = 320; spca50x->mode_cam[CIF].height = 240; spca50x->mode_cam[CIF].t_palette = P_JPEG | P_RAW | P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[CIF].pipe = 896; spca50x->mode_cam[CIF].method = 0; spca50x->mode_cam[CIF].mode = 2; spca50x->mode_cam[QPAL].width = 192; spca50x->mode_cam[QPAL].height = 144; spca50x->mode_cam[QPAL].t_palette = P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[QPAL].pipe = 896; spca50x->mode_cam[QPAL].method = 1; spca50x->mode_cam[QPAL].mode = 2; spca50x->mode_cam[QSIF].width = 176; spca50x->mode_cam[QSIF].height = 144; spca50x->mode_cam[QSIF].t_palette = P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[QSIF].pipe = 896; spca50x->mode_cam[QSIF].method = 1; spca50x->mode_cam[QSIF].mode = 2; spca50x->qindex = 1; // set quantization table return 0; } pwcbsd/spca5xx-20060402/drivers/usb/ov7630c.h000744 000423 000000 00000041534 10546676141 020635 0ustar00luigiwheel000000 000000 /**************************************************************************** # Omnivision OV7630c library # # Copyright (C) 2006 Michel Xhaard mxhaard@magic.fr # # # # This program is free software; you can redistribute it and/or modify # # it under the terms of the GNU General Public License as published by # # the Free Software Foundation; either version 2 of the License, or # # (at your option) any later version. # # # # This program is distributed in the hope that it will be useful, # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # # ****************************************************************************/ static __u16 ov7630c_start_data[][3] = { {0xa0, 0x0001, 0x0000}, {0xa0, 0x0010, 0x0002}, { 0xa0, 0x01, 0x0000 }, { 0xa0, 0x10, 0x0002 }, { 0xa0, 0x03, 0x0008 }, { 0xa0, 0x01, 0x0001 }, { 0xa0, 0x06, 0x0010 }, { 0xa0, 0xa1, 0x008b }, { 0xa0, 0x08, 0x008d }, { 0xa0, 0x02, 0x0003 }, { 0xa0, 0x80, 0x0004 }, { 0xa0, 0x01, 0x0005 }, { 0xa0, 0xe0, 0x0006 }, { 0xa0, 0x01, 0x0012 }, { 0xa0, 0x12, 0x0092 }, { 0xa0, 0x80, 0x0093 }, { 0xa0, 0x00, 0x0094 }, { 0xa0, 0x01, 0x0090 }, { 0xa1, 0x01, 0x0091 }, { 0xa0, 0x02, 0x0083 }, { 0xa0, 0x01, 0x0085 }, { 0xa0, 0x90, 0x0086 }, { 0xa0, 0x91, 0x0087 }, { 0xa0, 0x10, 0x0088 }, { 0xa0, 0x00, 0x0098 }, { 0xa0, 0x00, 0x009a }, { 0xa0, 0x00, 0x011a }, { 0xa0, 0x00, 0x011c }, { 0xa0, 0xd8, 0x009c }, { 0xa0, 0x88, 0x009e }, { 0xa0, 0x12, 0x0092 }, { 0xa0, 0x69, 0x0093 }, { 0xa0, 0x00, 0x0094 }, { 0xa0, 0x01, 0x0090 }, { 0xa1, 0x01, 0x0091 }, { 0xa0, 0x04, 0x0092 }, { 0xa0, 0x20, 0x0093 }, { 0xa0, 0x00, 0x0094 }, { 0xa0, 0x01, 0x0090 }, { 0xa1, 0x01, 0x0091 }, { 0xa0, 0x06, 0x0092 }, { 0xa0, 0x50, 0x0093 }, { 0xa0, 0x00, 0x0094 }, { 0xa0, 0x01, 0x0090 }, { 0xa1, 0x01, 0x0091 }, { 0xa0, 0x13, 0x0092 }, { 0xa0, 0x83, 0x0093 }, { 0xa0, 0x00, 0x0094 }, { 0xa0, 0x01, 0x0090 }, { 0xa1, 0x01, 0x0091 }, { 0xa0, 0x14, 0x0092 }, { 0xa0, 0x00, 0x0093 }, { 0xa0, 0x00, 0x0094 }, { 0xa0, 0x01, 0x0090 }, { 0xa1, 0x01, 0x0091 }, { 0xa0, 0x15, 0x0092 }, { 0xa0, 0x24, 0x0093 }, { 0xa0, 0x00, 0x0094 }, { 0xa0, 0x01, 0x0090 }, { 0xa1, 0x01, 0x0091 }, { 0xa0, 0x17, 0x0092 }, { 0xa0, 0x18, 0x0093 }, { 0xa0, 0x00, 0x0094 }, { 0xa0, 0x01, 0x0090 }, { 0xa1, 0x01, 0x0091 }, { 0xa0, 0x18, 0x0092 }, { 0xa0, 0xba, 0x0093 }, { 0xa0, 0x00, 0x0094 }, { 0xa0, 0x01, 0x0090 }, { 0xa1, 0x01, 0x0091 }, { 0xa0, 0x19, 0x0092 }, { 0xa0, 0x02, 0x0093 }, { 0xa0, 0x00, 0x0094 }, { 0xa0, 0x01, 0x0090 }, { 0xa1, 0x01, 0x0091 }, { 0xa0, 0x1a, 0x0092 }, { 0xa0, 0xf6, 0x0093 }, { 0xa0, 0x00, 0x0094 }, { 0xa0, 0x01, 0x0090 }, { 0xa1, 0x01, 0x0091 }, { 0xa0, 0x1b, 0x0092 }, { 0xa0, 0x02, 0x0093 }, { 0xa0, 0x00, 0x0094 }, { 0xa0, 0x01, 0x0090 }, { 0xa1, 0x01, 0x0091 }, { 0xa0, 0x20, 0x0092 }, { 0xa0, 0xc2, 0x0093 }, { 0xa0, 0x00, 0x0094 }, { 0xa0, 0x01, 0x0090 }, { 0xa1, 0x01, 0x0091 }, { 0xa0, 0x24, 0x0092 }, { 0xa0, 0x60, 0x0093 }, { 0xa0, 0x00, 0x0094 }, { 0xa0, 0x01, 0x0090 }, { 0xa1, 0x01, 0x0091 }, { 0xa0, 0x25, 0x0092 }, { 0xa0, 0x40, 0x0093 }, { 0xa0, 0x00, 0x0094 }, { 0xa0, 0x01, 0x0090 }, { 0xa1, 0x01, 0x0091 }, { 0xa0, 0x26, 0x0092 }, { 0xa0, 0x30, 0x0093 }, { 0xa0, 0x00, 0x0094 }, { 0xa0, 0x01, 0x0090 }, { 0xa1, 0x01, 0x0091 }, { 0xa0, 0x27, 0x0092 }, { 0xa0, 0xea, 0x0093 }, { 0xa0, 0x00, 0x0094 }, { 0xa0, 0x01, 0x0090 }, { 0xa1, 0x01, 0x0091 }, { 0xa0, 0x28, 0x0092 }, { 0xa0, 0xa0, 0x0093 }, { 0xa0, 0x00, 0x0094 }, { 0xa0, 0x01, 0x0090 }, { 0xa1, 0x01, 0x0091 }, { 0xa0, 0x21, 0x0092 }, { 0xa0, 0x00, 0x0093 }, { 0xa0, 0x00, 0x0094 }, { 0xa0, 0x01, 0x0090 }, { 0xa1, 0x01, 0x0091 }, { 0xa0, 0x2a, 0x0092 }, { 0xa0, 0x81, 0x0093 }, { 0xa0, 0x00, 0x0094 }, { 0xa0, 0x01, 0x0090 }, { 0xa1, 0x01, 0x0091 }, { 0xa0, 0x2b, 0x0092 }, { 0xa0, 0x96, 0x0093 }, { 0xa0, 0x00, 0x0094 }, { 0xa0, 0x01, 0x0090 }, { 0xa1, 0x01, 0x0091 }, { 0xa0, 0x2d, 0x0092 }, { 0xa0, 0x94, 0x0093 }, { 0xa0, 0x00, 0x0094 }, { 0xa0, 0x01, 0x0090 }, { 0xa1, 0x01, 0x0091 }, { 0xa0, 0x2f, 0x0092 }, { 0xa0, 0x3d, 0x0093 }, { 0xa0, 0x00, 0x0094 }, { 0xa0, 0x01, 0x0090 }, { 0xa1, 0x01, 0x0091 }, { 0xa0, 0x30, 0x0092 }, { 0xa0, 0x24, 0x0093 }, { 0xa0, 0x00, 0x0094 }, { 0xa0, 0x01, 0x0090 }, { 0xa1, 0x01, 0x0091 }, { 0xa0, 0x60, 0x0092 }, { 0xa0, 0x00, 0x0093 }, { 0xa0, 0x00, 0x0094 }, { 0xa0, 0x01, 0x0090 }, { 0xa1, 0x01, 0x0091 }, { 0xa0, 0x61, 0x0092 }, { 0xa0, 0x40, 0x0093 }, { 0xa0, 0x00, 0x0094 }, { 0xa0, 0x01, 0x0090 }, { 0xa1, 0x01, 0x0091 }, { 0xa0, 0x68, 0x0092 }, { 0xa0, 0x7c, 0x0093 }, { 0xa0, 0x00, 0x0094 }, { 0xa0, 0x01, 0x0090 }, { 0xa1, 0x01, 0x0091 }, { 0xa0, 0x6f, 0x0092 }, { 0xa0, 0x15, 0x0093 }, { 0xa0, 0x00, 0x0094 }, { 0xa0, 0x01, 0x0090 }, { 0xa1, 0x01, 0x0091 }, { 0xa0, 0x75, 0x0092 }, { 0xa0, 0x88, 0x0093 }, { 0xa0, 0x00, 0x0094 }, { 0xa0, 0x01, 0x0090 }, { 0xa1, 0x01, 0x0091 }, { 0xa0, 0x77, 0x0092 }, { 0xa0, 0xb5, 0x0093 }, { 0xa0, 0x00, 0x0094 }, { 0xa0, 0x01, 0x0090 }, { 0xa1, 0x01, 0x0091 }, { 0xa0, 0x01, 0x0092 }, { 0xa0, 0x60, 0x0093 }, { 0xa0, 0x00, 0x0094 }, { 0xa0, 0x01, 0x0090 }, { 0xa1, 0x01, 0x0091 }, { 0xa0, 0x02, 0x0092 }, { 0xa0, 0x60, 0x0093 }, { 0xa0, 0x00, 0x0094 }, { 0xa0, 0x01, 0x0090 }, { 0xa1, 0x01, 0x0091 }, { 0xa0, 0x05, 0x0012 }, { 0xa0, 0x77, 0x0101 }, { 0xa0, 0x0d, 0x0100 }, { 0xa0, 0x06, 0x0189 }, { 0xa0, 0x04, 0x01a7 }, { 0xa0, 0x00, 0x01ad }, { 0xa0, 0x03, 0x01c5 }, { 0xa0, 0x13, 0x01cb }, { 0xa0, 0x08, 0x0250 }, { 0xa0, 0x08, 0x0301 }, { 0xa0, 0x60, 0x0116 }, { 0xa0, 0x46, 0x0118 }, { 0xa0, 0x04, 0x0113 }, //0x10, { 0xa1, 0x01, 0x0002 }, { 0xa0, 0x50, 0x010a }, { 0xa0, 0xf8, 0x010b }, { 0xa0, 0xf8, 0x010c }, { 0xa0, 0xf8, 0x010d }, { 0xa0, 0x50, 0x010e }, { 0xa0, 0xf8, 0x010f }, { 0xa0, 0xf8, 0x0110 }, { 0xa0, 0xf8, 0x0111 }, { 0xa0, 0x50, 0x0112 }, //0x03, { 0xa1, 0x01, 0x0008 }, { 0xa0, 0x03, 0x0008 }, { 0xa0, 0x08, 0x01c6 }, //0x05, { 0xa1, 0x01, 0x01c8 }, //0x07, { 0xa1, 0x01, 0x01c9 }, //0x0f, { 0xa1, 0x01, 0x01ca }, { 0xa0, 0x0f, 0x01cb }, { 0xa0, 0x01, 0x0120 }, { 0xa0, 0x0c, 0x0121 }, { 0xa0, 0x1f, 0x0122 }, { 0xa0, 0x3a, 0x0123 }, { 0xa0, 0x53, 0x0124 }, { 0xa0, 0x6d, 0x0125 }, { 0xa0, 0x85, 0x0126 }, { 0xa0, 0x9c, 0x0127 }, { 0xa0, 0xb0, 0x0128 }, { 0xa0, 0xc2, 0x0129 }, { 0xa0, 0xd1, 0x012a }, { 0xa0, 0xde, 0x012b }, { 0xa0, 0xe9, 0x012c }, { 0xa0, 0xf2, 0x012d }, { 0xa0, 0xf9, 0x012e }, { 0xa0, 0xff, 0x012f }, { 0xa0, 0x05, 0x0130 }, { 0xa0, 0x0f, 0x0131 }, { 0xa0, 0x16, 0x0132 }, { 0xa0, 0x1a, 0x0133 }, { 0xa0, 0x19, 0x0134 }, { 0xa0, 0x19, 0x0135 }, { 0xa0, 0x17, 0x0136 }, { 0xa0, 0x15, 0x0137 }, { 0xa0, 0x12, 0x0138 }, { 0xa0, 0x10, 0x0139 }, { 0xa0, 0x0e, 0x013a }, { 0xa0, 0x0b, 0x013b }, { 0xa0, 0x09, 0x013c }, { 0xa0, 0x08, 0x013d }, { 0xa0, 0x06, 0x013e }, { 0xa0, 0x03, 0x013f }, { 0xa0, 0x50, 0x010a }, { 0xa0, 0xf8, 0x010b }, { 0xa0, 0xf8, 0x010c }, { 0xa0, 0xf8, 0x010d }, { 0xa0, 0x50, 0x010e }, { 0xa0, 0xf8, 0x010f }, { 0xa0, 0xf8, 0x0110 }, { 0xa0, 0xf8, 0x0111 }, { 0xa0, 0x50, 0x0112 }, { 0xa1, 0x01, 0x0180 }, { 0xa0, 0x00, 0x0180 }, { 0xa0, 0x10, 0x0092 }, { 0xa0, 0x1b, 0x0093 }, { 0xa0, 0x00, 0x0094 }, { 0xa0, 0x01, 0x0090 }, { 0xa1, 0x01, 0x0091 }, { 0xa0, 0x76, 0x0092 }, { 0xa0, 0x02, 0x0093 }, { 0xa0, 0x00, 0x0094 }, { 0xa0, 0x01, 0x0090 }, { 0xa1, 0x01, 0x0091 }, { 0xa0, 0x2a, 0x0092 }, { 0xa0, 0x81, 0x0093 }, { 0xa0, 0x00, 0x0094 }, { 0xa0, 0x01, 0x0090 }, { 0xa1, 0x01, 0x0091 }, { 0xa0, 0x2b, 0x0092 }, { 0xa0, 0x00, 0x0093 }, { 0xa0, 0x00, 0x0094 }, { 0xa0, 0x01, 0x0090 }, { 0xa1, 0x01, 0x0091 }, { 0xa0, 0x00, 0x0190 }, { 0xa0, 0x01, 0x0191 }, { 0xa0, 0xb8, 0x0192 }, { 0xa0, 0x00, 0x0195 }, { 0xa0, 0x00, 0x0196 }, { 0xa0, 0x37, 0x0197 }, { 0xa0, 0x10, 0x018c }, { 0xa0, 0x20, 0x018f }, { 0xa0, 0x10, 0x01a9 }, { 0xa0, 0x26, 0x01aa }, { 0xa0, 0x50, 0x011d }, { 0xa0, 0x02, 0x0180 }, { 0xa0, 0x40, 0x0180 }, { 0xa0, 0x13, 0x0092 }, { 0xa0, 0x83, 0x0093 }, { 0xa0, 0x00, 0x0094 }, { 0xa0, 0x01, 0x0090 }, { 0xa1, 0x01, 0x0091 }, //40 { 0xa1, 0x01, 0x0180 }, { 0xa0, 0x42, 0x0180 }, {0, 0, 0} }; static __u16 ov7630c_scale_data[][3] = { {0xa0, 0x0001, 0x0000}, {0xa0, 0x0000, 0x0002}, {0xa0, 0x0003, 0x0008}, {0xa0, 0x0001, 0x0001}, {0xa0, 0x0006, 0x0010}, {0xa0, 0x00a1, 0x008b}, {0xa0, 0x0008, 0x008d}, {0xa0, 0x0002, 0x0003}, {0xa0, 0x0080, 0x0004}, {0xa0, 0x0001, 0x0005}, {0xa0, 0x00e0, 0x0006}, {0xa0, 0x0001, 0x0012}, {0xa0, 0x0012, 0x0092}, {0xa0, 0x0080, 0x0093}, {0xa0, 0x0000, 0x0094}, {0xa0, 0x0001, 0x0090}, {0xa1, 0x0001, 0x0091}, {0xa0, 0x0002, 0x0083}, {0xa0, 0x0001, 0x0085}, {0xa0, 0x0090, 0x0086}, {0xa0, 0x0091, 0x0087}, {0xa0, 0x0010, 0x0088}, {0xa0, 0x0000, 0x0098}, {0xa0, 0x0000, 0x009a}, {0xa0, 0x0000, 0x011a}, {0xa0, 0x0000, 0x011c}, {0xa0, 0x00e6, 0x009c}, {0xa0, 0x0086, 0x009e}, /* i2c */ {0xa0, 0x0012, 0x0092}, {0xa0, 0x0069, 0x0093}, {0xa0, 0x0000, 0x0094}, {0xa0, 0x0001, 0x0090}, {0xa1, 0x0001, 0x0091}, {0xa0, 0x0004, 0x0092}, {0xa0, 0x0020, 0x0093}, {0xa0, 0x0000, 0x0094}, {0xa0, 0x0001, 0x0090}, {0xa1, 0x0001, 0x0091}, {0xa0, 0x0006, 0x0092}, {0xa0, 0x0050, 0x0093}, {0xa0, 0x0000, 0x0094}, {0xa0, 0x0001, 0x0090}, {0xa1, 0x0001, 0x0091}, {0xa0, 0x0013, 0x0092}, {0xa0, 0x00c3, 0x0093}, {0xa0, 0x0000, 0x0094}, {0xa0, 0x0001, 0x0090}, {0xa1, 0x0001, 0x0091}, {0xa0, 0x0014, 0x0092}, {0xa0, 0x0000, 0x0093}, {0xa0, 0x0000, 0x0094}, {0xa0, 0x0001, 0x0090}, {0xa1, 0x0001, 0x0091}, {0xa0, 0x0015, 0x0092}, {0xa0, 0x0024, 0x0093}, {0xa0, 0x0000, 0x0094}, {0xa0, 0x0001, 0x0090}, {0xa1, 0x0001, 0x0091}, {0xa0, 0x0019, 0x0092}, {0xa0, 0x0003, 0x0093}, {0xa0, 0x0000, 0x0094}, {0xa0, 0x0001, 0x0090}, {0xa1, 0x0001, 0x0091}, {0xa0, 0x001a, 0x0092}, {0xa0, 0x00f6, 0x0093}, {0xa0, 0x0000, 0x0094}, {0xa0, 0x0001, 0x0090}, {0xa1, 0x0001, 0x0091}, {0xa0, 0x001b, 0x0092}, {0xa0, 0x0002, 0x0093}, {0xa0, 0x0000, 0x0094}, {0xa0, 0x0001, 0x0090}, {0xa1, 0x0001, 0x0091}, {0xa0, 0x0020, 0x0092}, {0xa0, 0x00c2, 0x0093}, {0xa0, 0x0000, 0x0094}, {0xa0, 0x0001, 0x0090}, {0xa1, 0x0001, 0x0091}, {0xa0, 0x0024, 0x0092}, {0xa0, 0x0060, 0x0093}, {0xa0, 0x0000, 0x0094}, {0xa0, 0x0001, 0x0090}, {0xa1, 0x0001, 0x0091}, {0xa0, 0x0025, 0x0092}, {0xa0, 0x0040, 0x0093}, {0xa0, 0x0000, 0x0094}, {0xa0, 0x0001, 0x0090}, {0xa1, 0x0001, 0x0091}, {0xa0, 0x0026, 0x0092}, {0xa0, 0x0030, 0x0093}, {0xa0, 0x0000, 0x0094}, {0xa0, 0x0001, 0x0090}, {0xa1, 0x0001, 0x0091}, {0xa0, 0x0027, 0x0092}, {0xa0, 0x00ea, 0x0093}, {0xa0, 0x0000, 0x0094}, {0xa0, 0x0001, 0x0090}, {0xa1, 0x0001, 0x0091}, {0xa0, 0x0028, 0x0092}, {0xa0, 0x00a0, 0x0093}, {0xa0, 0x0000, 0x0094}, {0xa0, 0x0001, 0x0090}, {0xa1, 0x0001, 0x0091}, {0xa0, 0x0021, 0x0092}, {0xa0, 0x0000, 0x0093}, {0xa0, 0x0000, 0x0094}, {0xa0, 0x0001, 0x0090}, {0xa1, 0x0001, 0x0091}, {0xa0, 0x002a, 0x0092}, {0xa0, 0x0081, 0x0093}, {0xa0, 0x0000, 0x0094}, {0xa0, 0x0001, 0x0090}, {0xa1, 0x0001, 0x0091}, {0xa0, 0x002b, 0x0092}, {0xa0, 0x0096, 0x0093}, {0xa0, 0x0000, 0x0094}, {0xa0, 0x0001, 0x0090}, {0xa1, 0x0001, 0x0091}, {0xa0, 0x002d, 0x0092}, {0xa0, 0x0084, 0x0093}, {0xa0, 0x0000, 0x0094}, {0xa0, 0x0001, 0x0090}, {0xa1, 0x0001, 0x0091}, {0xa0, 0x002f, 0x0092}, {0xa0, 0x003d, 0x0093}, {0xa0, 0x0000, 0x0094}, {0xa0, 0x0001, 0x0090}, {0xa1, 0x0001, 0x0091}, {0xa0, 0x0030, 0x0092}, {0xa0, 0x0024, 0x0093}, {0xa0, 0x0000, 0x0094}, {0xa0, 0x0001, 0x0090}, {0xa1, 0x0001, 0x0091}, {0xa0, 0x0060, 0x0092}, {0xa0, 0x0000, 0x0093}, {0xa0, 0x0000, 0x0094}, {0xa0, 0x0001, 0x0090}, {0xa1, 0x0001, 0x0091}, {0xa0, 0x0061, 0x0092}, {0xa0, 0x0040, 0x0093}, {0xa0, 0x0000, 0x0094}, {0xa0, 0x0001, 0x0090}, {0xa1, 0x0001, 0x0091}, {0xa0, 0x0068, 0x0092}, {0xa0, 0x007c, 0x0093}, {0xa0, 0x0000, 0x0094}, {0xa0, 0x0001, 0x0090}, {0xa1, 0x0001, 0x0091}, {0xa0, 0x006f, 0x0092}, {0xa0, 0x0015, 0x0093}, {0xa0, 0x0000, 0x0094}, {0xa0, 0x0001, 0x0090}, {0xa1, 0x0001, 0x0091}, {0xa0, 0x0075, 0x0092}, {0xa0, 0x0088, 0x0093}, {0xa0, 0x0000, 0x0094}, {0xa0, 0x0001, 0x0090}, {0xa1, 0x0001, 0x0091}, {0xa0, 0x0077, 0x0092}, {0xa0, 0x00b5, 0x0093}, {0xa0, 0x0000, 0x0094}, {0xa0, 0x0001, 0x0090}, {0xa1, 0x0001, 0x0091}, {0xa0, 0x0001, 0x0092}, {0xa0, 0x0060, 0x0093}, {0xa0, 0x0000, 0x0094}, {0xa0, 0x0001, 0x0090}, {0xa1, 0x0001, 0x0091}, {0xa0, 0x0002, 0x0092}, {0xa0, 0x0060, 0x0093}, {0xa0, 0x0000, 0x0094}, {0xa0, 0x0001, 0x0090}, {0xa1, 0x0001, 0x0091}, {0xa0, 0x0017, 0x0092}, {0xa0, 0x0018, 0x0093}, {0xa0, 0x0000, 0x0094}, {0xa0, 0x0001, 0x0090}, {0xa1, 0x0001, 0x0091}, {0xa0, 0x0018, 0x0092}, {0xa0, 0x00ba, 0x0093}, {0xa0, 0x0000, 0x0094}, {0xa0, 0x0001, 0x0090}, {0xa1, 0x0001, 0x0091}, {0xa0, 0x0005, 0x0012}, {0xa0, 0x0077, 0x0101}, {0xa0, 0x000d, 0x0100}, {0xa0, 0x0006, 0x0189}, {0xa0, 0x0004, 0x01a7}, {0xa0, 0x0000, 0x01ad}, {0xa0, 0x0003, 0x01c5}, {0xa0, 0x0013, 0x01cb}, {0xa0, 0x0008, 0x0250}, {0xa0, 0x0008, 0x0301}, {0xa0, 0x0060, 0x0116}, {0xa0, 0x0046, 0x0118}, {0xa0, 0x0004, 0x0113}, {0xa1, 0x0001, 0x0002}, {0xa0, 0x004e, 0x010a}, {0xa0, 0x00fe, 0x010b}, {0xa0, 0x00f4, 0x010c}, {0xa0, 0x00f7, 0x010d}, {0xa0, 0x004d, 0x010e}, {0xa0, 0x00fc, 0x010f}, {0xa0, 0x0000, 0x0110}, {0xa0, 0x00f6, 0x0111}, {0xa0, 0x004a, 0x0112}, {0xa1, 0x0001, 0x0008}, {0xa0, 0x0003, 0x0008}, {0xa0, 0x0008, 0x01c6}, {0xa1, 0x0001, 0x01c8}, {0xa1, 0x0001, 0x01c9}, {0xa1, 0x0001, 0x01ca}, {0xa0, 0x000f, 0x01cb}, {0xa0, 0x0016, 0x0120}, {0xa0, 0x003a, 0x0121}, {0xa0, 0x005b, 0x0122}, {0xa0, 0x007c, 0x0123}, {0xa0, 0x0094, 0x0124}, {0xa0, 0x00a9, 0x0125}, {0xa0, 0x00bb, 0x0126}, {0xa0, 0x00ca, 0x0127}, {0xa0, 0x00d7, 0x0128}, {0xa0, 0x00e1, 0x0129}, {0xa0, 0x00ea, 0x012a}, {0xa0, 0x00f1, 0x012b}, {0xa0, 0x00f7, 0x012c}, {0xa0, 0x00fc, 0x012d}, {0xa0, 0x00ff, 0x012e}, {0xa0, 0x00ff, 0x012f}, {0xa0, 0x0020, 0x0130}, {0xa0, 0x0022, 0x0131}, {0xa0, 0x0020, 0x0132}, {0xa0, 0x001c, 0x0133}, {0xa0, 0x0016, 0x0134}, {0xa0, 0x0013, 0x0135}, {0xa0, 0x0010, 0x0136}, {0xa0, 0x000d, 0x0137}, {0xa0, 0x000b, 0x0138}, {0xa0, 0x0009, 0x0139}, {0xa0, 0x0007, 0x013a}, {0xa0, 0x0006, 0x013b}, {0xa0, 0x0005, 0x013c}, {0xa0, 0x0004, 0x013d}, {0xa0, 0x0000, 0x013e}, {0xa0, 0x0001, 0x013f}, {0xa0, 0x004e, 0x010a}, {0xa0, 0x00fe, 0x010b}, {0xa0, 0x00f4, 0x010c}, {0xa0, 0x00f7, 0x010d}, {0xa0, 0x004d, 0x010e}, {0xa0, 0x00fc, 0x010f}, {0xa0, 0x0000, 0x0110}, {0xa0, 0x00f6, 0x0111}, {0xa0, 0x004a, 0x0112}, {0xa1, 0x0001, 0x0180}, {0xa0, 0x0000, 0x0180}, {0xa0, 0x0010, 0x0092}, {0xa0, 0x000d, 0x0093}, {0xa0, 0x0000, 0x0094}, {0xa0, 0x0001, 0x0090}, {0xa1, 0x0001, 0x0091}, {0xa0, 0x0076, 0x0092}, {0xa0, 0x0002, 0x0093}, {0xa0, 0x0000, 0x0094}, {0xa0, 0x0001, 0x0090}, {0xa1, 0x0001, 0x0091}, {0xa0, 0x002a, 0x0092}, {0xa0, 0x0081, 0x0093}, {0xa0, 0x0000, 0x0094}, {0xa0, 0x0001, 0x0090}, {0xa1, 0x0001, 0x0091}, {0xa0, 0x002b, 0x0092}, {0xa0, 0x0000, 0x0093}, {0xa0, 0x0000, 0x0094}, {0xa0, 0x0001, 0x0090}, {0xa1, 0x0001, 0x0091}, {0xa0, 0x0000, 0x0190}, {0xa0, 0x0000, 0x0191}, {0xa0, 0x00d8, 0x0192}, {0xa0, 0x0000, 0x0195}, {0xa0, 0x0000, 0x0196}, {0xa0, 0x001b, 0x0197}, {0xa0, 0x0010, 0x018c}, {0xa0, 0x0020, 0x018f}, {0xa0, 0x0010, 0x01a9}, {0xa0, 0x0026, 0x01aa}, {0xa0, 0x0050, 0x011d}, {0xa0, 0x0002, 0x0180}, {0xa0, 0x0040, 0x0180}, {0xa0, 0x0013, 0x0092}, {0xa0, 0x00c3, 0x0093}, {0xa0, 0x0000, 0x0094}, {0xa0, 0x0001, 0x0090}, {0xa1, 0x0001, 0x0091}, {0xa1, 0x0001, 0x0180}, {0xa0, 0x0042, 0x0180}, {0, 0, 0} }; pwcbsd/spca5xx-20060402/drivers/usb/pac207.h000755 000423 000000 00000043664 10553461760 020527 0ustar00luigiwheel000000 000000 #ifndef PAC207USB_H #define PAC207USB_H /**************************************************************************** # Pixart PAC207BCA library # # Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li # # Copyleft (C) 2005 Michel Xhaard mxhaard@magic.fr # # # # This program is free software; you can redistribute it and/or modify # # it under the terms of the GNU General Public License as published by # # the Free Software Foundation; either version 2 of the License, or # # (at your option) any later version. # # # # This program is distributed in the hope that it will be useful, # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # # ****************************************************************************/ /* * These are data written to the register 0x0001 starting * at offset 0x02.. 0x19 and 0x40..0x4b * I _think_ these coincide with the individual registers. */ enum { REG_ID0 = 0x00, /* sensor identifier */ REG_ID1 = 0x01, /* sensor identifier */ REG_PXCK = 0x02, /* pixel clock */ REG_EXPOSURE = 0x03, /* range: 0x12 (fast) 0x24 (slow) */ REG_BOH4 = 0x04, /* ??? */ REG_BRIGHTNESS = 0x08, /* brightness */ REG_CONTRAST = 0x0e, /* contrast - also global gain */ REG_POWER = 0x0f, /* power control */ REG_BIAS = 0x11, /* analog bias */ REG_LOAD = 0x13, /* sensor load */ REG_START = 0x40, /* start/stop isoc pipe */ REG_FORMAT = 0x41, /* format, led, compression */ REG_RATE = 0x42, /* rate control ? */ REG_BAL_SIZE = 0x4a, /* balance size (?) */ REG_RAM_TEST = 0x4b, /* sram test value */ }; static __u8 pac207_sensor_init[][8] = { {0x10, 0x12, 0x0d, 0x12, 0x0c, 0x01, 0x29, 0xf0}, //2 // {0x10, 0x24, 0x06, 0x12, 0x0c, 0x01, 0x29, 0xf0}, //2 increase the times exposure decrease frame rate {0x00, 0x64, 0x64, 0x64, 0x04, 0x10, 0xF0, 0x30}, //a reg_10 digital gain Red Green Blue Ggain {0x00, 0x00, 0x00, 0x70, 0xA0, 0xF8, 0x00, 0x00}, //12 {0x00, 0x00, 0x32, 0x00, 0x96, 0x00, 0xA2, 0x02}, //40 {0x32, 0x00, 0x96, 0x00, 0xA2, 0x02, 0xAF, 0x00}, //42 reg_66 rate control }; static __u8 PacReg72[] = { 0x00, 0x00, 0x36, 0x00 }; //48 reg_72 Rate Control end BalSize_4a =0x36 /******************* Camera Interface ***********************/ static __u16 pac207_getbrightness(struct usb_spca50x *spca50x); static __u16 pac207_getcontrast(struct usb_spca50x *spca50x); static void pac207_setbrightness(struct usb_spca50x *spca50x); static void pac207_setcontrast(struct usb_spca50x *spca50x); static int pac207_init(struct usb_spca50x *spca50x); static void pac207_start(struct usb_spca50x *spca50x); static void pac207_stop(struct usb_spca50x *spca50x); static int pac207_config(struct usb_spca50x *spca50x); static void pac207_shutdown(struct usb_spca50x *spca50x); static void pac207_setAutobright(struct usb_spca50x *spca50x); /******************* Camera Private ***********************/ static void pac207_reg_write(struct usb_device *dev, __u16 index, __u16 value); static void pac207_reg_read(struct usb_device *dev, __u16 index, __u8 * buffer); #define pac207RegRead(dev,req,value,index,buffer,length) \ usb_rd_vend_int(dev,req,value,index,buffer,length) #define pac207RegWrite(dev,req,value,index,buffer,length) \ usb_wr_vend_int(dev,req,value,index,buffer,length) /***************************** Implementation ****************************/ /* * Probably req = 00 means write a single value at index, * req = 01 means write the buffer sized. */ static void pac207_reg_read(struct usb_device *dev, __u16 index, __u8 * buffer) { pac207RegRead(dev, 0x00, 0x00, index, buffer, 1); return; } static void pac207_reg_write(struct usb_device *dev, __u16 index, __u16 value) { pac207RegWrite(dev, 0x00, value, index, NULL, 0); return; } /* push data to the sensor */ static void pac207_push(struct usb_spca50x *spca50x) { pac207_reg_write(spca50x->dev, 0x13, 0x01); //load registers to sensor (Bit 0, auto clear) pac207_reg_write(spca50x->dev, 0x1c, 0x01); //not documented } static __u16 pac207_getbrightness(struct usb_spca50x *spca50x) { __u8 brightness = 0; pac207_reg_read(spca50x->dev, 0x0008, &brightness); spca50x->brightness = brightness << 8; return spca50x->brightness; } static void pac207_setbrightness(struct usb_spca50x *spca50x) { __u8 brightness = spca50x->brightness >> 8; pac207_reg_write(spca50x->dev, 0x0008, brightness); pac207_push(spca50x); } static __u16 pac207_getcontrast(struct usb_spca50x *spca50x) { __u8 contrast = 0; pac207_reg_read(spca50x->dev, 0x000e, &contrast); spca50x->contrast = contrast << 11; return spca50x->contrast; } static void pac207_setcontrast(struct usb_spca50x *spca50x) { __u8 contrast = spca50x->contrast >> 11; pac207_reg_write(spca50x->dev, 0x000e, contrast); pac207_push(spca50x); } static int pac207_init(struct usb_spca50x *spca50x) { __u8 id[] = { 0, 0 }; pac207_reg_write(spca50x->dev, 0x41, 0x00); //Turn of LED pac207_reg_read(spca50x->dev, 0x0000, &id[0]); pac207_reg_read(spca50x->dev, 0x0001, &id[1]); id[0] = ((id[0] & 0x0F) << 4) | ((id[1] & 0xf0) >> 4); id[1] = id[1] & 0x0f; PDEBUG(2, " Pixart Sensor ID 0x%02X Chips ID 0x%02X !!\n", id[0], id[1]); if (id[0] != 0x27 || id[1] != 0x00) return -ENODEV; return 0; } /* * Fill the table of formats. * This camera has 2 native formats - CIF (mode=0) and QCIF (mode=1), * the remaining ones are obtained by cropping. */ static void set_pac207SIF(struct usb_spca50x *spca50x) { memset(spca50x->mode_cam, 0x00, TOTMODE * sizeof(struct mwebcam)); spca50x->mode_cam[CIF].width = 352; spca50x->mode_cam[CIF].height = 288; spca50x->mode_cam[CIF].t_palette = P_RAW | P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[CIF].pipe = 1023; spca50x->mode_cam[CIF].method = 0; spca50x->mode_cam[CIF].mode = 0; spca50x->mode_cam[QCIF].width = 176; spca50x->mode_cam[QCIF].height = 144; spca50x->mode_cam[QCIF].t_palette = P_RAW | P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[QCIF].pipe = 1023; spca50x->mode_cam[QCIF].method = 0; /* native */ spca50x->mode_cam[QCIF].mode = 1; #if 0 spca50x->mode_cam[SIF].width = 320; spca50x->mode_cam[SIF].height = 240; spca50x->mode_cam[SIF].t_palette = P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[SIF].pipe = 1023; spca50x->mode_cam[SIF].method = 1; /* crop */ spca50x->mode_cam[SIF].mode = 0; spca50x->mode_cam[QPAL].width = 192; spca50x->mode_cam[QPAL].height = 144; spca50x->mode_cam[QPAL].t_palette = P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[QPAL].pipe = 1023; spca50x->mode_cam[QPAL].method = 1; /* crop */ spca50x->mode_cam[QPAL].mode = 0; spca50x->mode_cam[QSIF].width = 160; spca50x->mode_cam[QSIF].height = 120; spca50x->mode_cam[QSIF].t_palette = P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[QSIF].pipe = 1023; spca50x->mode_cam[QSIF].method = 1; spca50x->mode_cam[QSIF].mode = 1; #endif return; } /* * Configurations for the em2820 alt 0 [0] endp 1 size 0: 1 attr 0x3 interval 11 [1] endp 2 size 0: 0 attr 0x1 interval 1 [2] endp 4 size 0: 512 attr 0x2 interval 1 alt 1 [0] endp 1 size 0: 1 attr 0x3 interval 11 [1] endp 2 size 0:1024 attr 0x1 interval 1 1024 [2] endp 4 size 0: 512 attr 0x2 interval 1 alt 2 [0] endp 1 size 0: 1 attr 0x3 interval 11 [1] endp 2 size 1: 724 attr 0x1 interval 1 1448 [2] endp 4 size 0: 512 attr 0x2 interval 1 alt 3 [0] endp 1 size 0: 1 attr 0x3 interval 11 [1] endp 2 size 1:1024 attr 0x1 interval 1 2048 [2] endp 4 size 0: 512 attr 0x2 interval 1 alt 4 [0] endp 1 size 0: 1 attr 0x3 interval 11 [1] endp 2 size 2: 768 attr 0x1 interval 1 2304 [2] endp 4 size 0: 512 attr 0x2 interval 1 alt 5 [0] endp 1 size 0: 1 attr 0x3 interval 11 [1] endp 2 size 2: 860 attr 0x1 interval 1 2580 [2] endp 4 size 0: 512 attr 0x2 interval 1 alt 6 [0] endp 1 size 0: 1 attr 0x3 interval 11 [1] endp 2 size 2: 964 attr 0x1 interval 1 2892 [2] endp 4 size 0: 512 attr 0x2 interval 1 alt 7 [0] endp 1 size 0: 1 attr 0x3 interval 11 [1] endp 2 size 2:1024 attr 0x1 interval 1 3072 [2] endp 4 size 0: 512 attr 0x2 interval 1 */ /*** a trace of accesses to the em28xx regs time rw data r10 00 alt5 r0c 0 w20 0 w22 0 r12 26 w12 26 r0c 0 w0c 0 r08 f7 w08 f7 w06 40 r0f 07 w0f 08 w15 20 w16 20 w17 20 w18 00 w19 00 w1a 00 w23 00 w24 00 w26 00 w13 00 w12 27 w0c 10 w27 34 20584 w10 0d r11 00 w11 00 r0c 10 w28 1c w29 84 w2a 14 w2b 64 w1c 00 w1d 00 w1e a0 width w1f 78 height r1b 00 w1b 00 r1b 00 w1b 00 we0 00 00 00 we3 00 00 00 we6 00 00 00 we9 00 00 00 wec 00 00 00 wef 00 00 00 wf2 00 00 00 wf5 00 00 00 wf8 00 00 00 wfb 00 00 00 w13 0c r0f 08 w0f 08 wba 0d 00 01 r05 00 wba 0d 00 00 r05 00 wba 01 00 08 r05 00 wba 02 00 14 r05 00 wba 03 01 e0 r05 00 wba 04 02 80 r05 00 wba 05 00 01 20598 ... 20623 r27 34 w27 34 r0c 10 w0c 10 r12 27 w12 67 w22 1c w20 13 r0c 10 w0c 10 reset pipe get current frame setup isoc (3 times) ***/ /* em28xx registers */ #define CHIPID_REG 0x0a #define USBSUSP_REG 0x0c /* */ #define AUDIOSRC_REG 0x0e #define XCLK_REG 0x0f #define VINMODE_REG 0x10 #define VINCTRL_REG 0x11 #define VINENABLE_REG 0x12 /* */ #define GAMMA_REG 0x14 #define RGAIN_REG 0x15 #define GGAIN_REG 0x16 #define BGAIN_REG 0x17 #define ROFFSET_REG 0x18 #define GOFFSET_REG 0x19 #define BOFFSET_REG 0x1a #define OFLOW_REG 0x1b #define HSTART_REG 0x1c #define VSTART_REG 0x1d #define CWIDTH_REG 0x1e #define CHEIGHT_REG 0x1f #define YGAIN_REG 0x20 #define YOFFSET_REG 0x21 #define UVGAIN_REG 0x22 #define UOFFSET_REG 0x23 #define VOFFSET_REG 0x24 #define SHARPNESS_REG 0x25 #define COMPR_REG 0x26 #define OUTFMT_REG 0x27 #define XMIN_REG 0x28 #define XMAX_REG 0x29 #define YMIN_REG 0x2a #define YMAX_REG 0x2b #define HSCALELOW_REG 0x30 #define HSCALEHIGH_REG 0x31 #define VSCALELOW_REG 0x32 #define VSCALEHIGH_REG 0x33 #define AC97LSB_REG 0x40 #define AC97MSB_REG 0x41 #define AC97ADDR_REG 0x42 #define AC97BUSY_REG 0x43 /* em202 registers */ #define MASTER_AC97 0x02 #define VIDEO_AC97 0x14 /* register settings */ #define EM28XX_AUDIO_SRC_TUNER 0xc0 #define EM28XX_AUDIO_SRC_LINE 0x80 #define _W(reg, buf, len) em28xx_write_regs(dev, reg, buf, len) #define _R(reg) em28xx_read_reg(dev, reg) static int em28xx_outfmt_set_yuv422(struct usb_device *dev) { _W(OUTFMT_REG, "\x34", 1); _W(VINMODE_REG, "\x10", 1); _W(VINCTRL_REG, "\x11", 1); return 0; } static int em2820_init(struct usb_spca50x *spca50x) { int i; struct usb_device *dev = spca50x->dev; printf("%s: to be done\n", __FUNCTION__); _W(0x08, "\x7d", 1); // reset through GPIO? /* Sets I2C speed to 100 KHz */ _W(0x06, "\x40", 1); em28xx_outfmt_set_yuv422(spca50x->dev); printf("chipid 0x%x\n", _R(CHIPID_REG) ); printf("format 0x%x\n", _R(OUTFMT_REG) ); _W(CWIDTH_REG, "\xa0", 1); _W(CHEIGHT_REG, "\x78", 1); printf("cwidth 0x%x\n", _R(CWIDTH_REG) ); printf("cheight 0x%x\n", _R(CHEIGHT_REG) ); _W(VINENABLE_REG, "\x67", 1); // video in enable ? */ _W(0x08, "\xf7", 1); // boh _W(0x0c, "\x10", 1); // start? _W(0x0c, "\x10", 1); // start? i = 0; /* silence compiler */ #if 0 for (i=0; i < 255; i++) { printf("em28xx reg 0x%02x -> 0x%02x\n", i, em28xx_read_reg(spca50x->dev, i)); } #endif return 0; } static int em2820_config(struct usb_spca50x *spca50x) { printf("%s: XXX not complete yet\n", __FUNCTION__); memset(spca50x->mode_cam, 0x00, TOTMODE * sizeof(struct mwebcam)); spca50x->mode_cam[CIF].width = 352; spca50x->mode_cam[CIF].height = 288; spca50x->mode_cam[CIF].t_palette = P_RAW | P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[CIF].pipe = 2580; /* alt 5 */ spca50x->mode_cam[CIF].method = 0; spca50x->mode_cam[CIF].mode = 0; spca50x->mode_cam[VGA].width = 352; spca50x->mode_cam[VGA].height = 288; spca50x->mode_cam[VGA].t_palette = P_RAW | P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[VGA].pipe = 2580; /* alt 5 */ spca50x->mode_cam[VGA].method = 0; spca50x->mode_cam[VGA].mode = 0; return 0; } static int pac207_config(struct usb_spca50x *spca50x) { PDEBUG(2, "Find Sensor PAC207"); PWC_SC(spca50x)->pwc_info.sensor = SENSOR_PAC207; set_pac207SIF(spca50x); pac207_reg_write(spca50x->dev, 0x41, 0x00); // 00 Bit_0=Image Format, Bit_1=LED, Bit_2=Compression test mode enable pac207_reg_write(spca50x->dev, 0x0f, 0x00); //Power Control pac207_reg_write(spca50x->dev, 0x11, 0x30); //Analog Bias return 0; } static void pac207_start(struct usb_spca50x *spca50x) { __u8 buffer; __u8 mode; spca50x->compress=1; pac207_reg_write(spca50x->dev, 0x0f, 0x10); //Power control (Bit 6-0) pac207RegWrite(spca50x->dev, 0x01, 0, 0x0002, pac207_sensor_init[0], 8); pac207RegWrite(spca50x->dev, 0x01, 0, 0x000a, pac207_sensor_init[1], 8); pac207RegWrite(spca50x->dev, 0x01, 0, 0x0012, pac207_sensor_init[2], 8); pac207RegWrite(spca50x->dev, 0x01, 0, 0x0040, pac207_sensor_init[3], 8); pac207RegWrite(spca50x->dev, 0x01, 0, 0x0042, pac207_sensor_init[4], 8); pac207RegWrite(spca50x->dev, 0x01, 0, 0x0048, PacReg72, 4); if (spca50x->compress) { // pac207_reg_write(spca50x->dev, 0x4a, 0x88); //Compression Balance size 0x88 pac207_reg_write(spca50x->dev, 0x4a, 0x88); //cOMPression Balance size 0x88 } else { pac207_reg_write(spca50x->dev, 0x4a, 0xff); //Compression Balance size } pac207_reg_write(spca50x->dev, 0x4b, 0x00); //Sram test value pac207_push(spca50x); pac207_reg_write(spca50x->dev, 0x41, 0x02); //Image Format (Bit 0), LED (Bit 1), Compression test mode enable (Bit 2) if (spca50x->mode) { /* 176x144 */ pac207_reg_read(spca50x->dev, 0x41, &buffer); mode = buffer | 0x01; //mode = buffer | 0x00; pac207_reg_write(spca50x->dev, 0x41, mode); //Set mode pac207_reg_write(spca50x->dev, 0x02, 0x04); //PXCK = 12MHz /n pac207_reg_write(spca50x->dev, 0x0e, 0x0f); //PGA global gain (Bit 4-0) PDEBUG(1, "pac207_start mode 176x144, mode = %x", mode); } else { /* 352x288 */ pac207_reg_read(spca50x->dev, 0x41, &buffer); mode = buffer & 0xfe; pac207_reg_write(spca50x->dev, 0x41, mode); //Set mode if (spca50x->compress) { pac207_reg_write(spca50x->dev, 0x02, 0x04); //PXCK = 12MHz / n 04 } else { pac207_reg_write(spca50x->dev, 0x02, 0x0a); //PXCK = 12MHz / n /* XXX was 0a */ } // pac207_reg_write(spca50x->dev, 0x0e, 0x04); //PGA global gain (Bit 4-0) higher=brighter pac207_reg_write(spca50x->dev, 0x0e, 0x0f); //PGA global gain (Bit 4-0) PDEBUG(1, "pac207_start mode 352x288, mode = %x", mode); } pac207_push(spca50x); udelay(1000); pac207_reg_write(spca50x->dev, 0x40, 0x01); //Start ISO pipe // pac207_setbrightness(spca50x); return; } static void pac207_stop(struct usb_spca50x *spca50x) { pac207_reg_write(spca50x->dev, 0x40, 0x00); //Stop ISO pipe pac207_reg_write(spca50x->dev, 0x41, 0x00); //Turn of LED pac207_reg_write(spca50x->dev, 0x0f, 0x00); //Power Control return; } static void pac207_shutdown(struct usb_spca50x *spca50x) { pac207_reg_write(spca50x->dev, 0x41, 0x00); //Turn of LED pac207_reg_write(spca50x->dev, 0x0f, 0x00); //Power Control return; } #ifdef SPCA5XX_ENABLE_REGISTERPLAY /* XXX i think this code is bigus, does it use global vars ? */ static void pac207_RegRead(struct usb_spca50x *spca50x) { __u8 buffer; RegAddress = RegAddress & 0xff; pac207_reg_read(spca50x->dev, RegAddress, &buffer); RegValue = buffer; PDEBUG(0, "pac207_ReadReg, Reg 0x%02X value = %x", RegAddress, RegValue); return; } static void pac207_RegWrite(struct usb_spca50x *spca50x) { __u8 buffer; RegAddress = RegAddress & 0xff; buffer = RegValue & 0xff; pac207_reg_write(spca50x->dev, RegAddress, buffer); pac207_push(spca50x->dev); PDEBUG(0, "pac207_WriteReg,Reg 0x%02X value = %x", RegAddress, buffer); return; } #endif #define BLIMIT(bright) (__u8)((bright>0x1a)?0x1a:((bright < 4)? 4:bright)) static void pac207_setAutobright(struct usb_spca50x *spca50x) { unsigned long flags; __u8 luma = 0; __u8 luma_mean = 128; __u8 luma_delta = 20; __u8 spring = 5; __u8 Pxclk; int Gbright = 0; pac207_reg_read(spca50x->dev, 0x02, &Pxclk); Gbright = Pxclk; spin_lock_irqsave(&spca50x->v4l_lock, flags); luma = spca50x->avg_lum; spin_unlock_irqrestore(&spca50x->v4l_lock, flags); PDEBUG(2, "Pac207 lumamean %d", luma); if ((luma < (luma_mean - luma_delta)) || (luma > (luma_mean + luma_delta))) { Gbright += ((luma_mean - luma) >> spring); Gbright = BLIMIT(Gbright); PDEBUG(2, "Pac207 Gbright %d", Gbright); pac207_reg_write(spca50x->dev, 0x02,(__u8) Gbright); pac207_reg_write(spca50x->dev, 0x13, 0x01); //load registers to sensor (Bit 0, auto clear) pac207_reg_write(spca50x->dev, 0x1c, 0x01); //not documented } } #undef BLIMIT /* SPCA5XX_ENABLE_REGISTERPLAY */ #endif // PAC207USB_H pwcbsd/spca5xx-20060402/drivers/usb/pas106b.h000744 000423 000000 00000033140 10546676141 020674 0ustar00luigiwheel000000 000000 /**************************************************************************** # PAS106B library # # Copyright (C) 2005 Thomas Kaiser thomas@kaiser.linux-site.net # # # # This program is free software; you can redistribute it and/or modify # # it under the terms of the GNU General Public License as published by # # the Free Software Foundation; either version 2 of the License, or # # (at your option) any later version. # # # # This program is distributed in the hope that it will be useful, # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # # ****************************************************************************/ static __u16 pas106b_com_data[][3] = { /* 352x288 */ /* Sream and Sensor specific */ {0xA1, 0x01, 0x0010}, //CMOSSensorSelect /* System */ {0xA0, 0x01, 0x0000}, //SystemControl {0xA0, 0x01, 0x0000}, //SystemControl /* Picture size */ {0xA0, 0x00, 0x0002}, //ClockSelect {0xA0, 0x03, 0x003a}, {0xA0, 0x0c, 0x003b}, {0xa0, 0x04, 0x0038}, {0, 0, 0} }; static __u16 pas106b_start_data[][3] = { /* 176x144 */ /* JPEG control */ {0xA0, 0x03, 0x0008}, //ClockSetting /* Sream and Sensor specific */ {0xA0, 0x0F, 0x0010}, //CMOSSensorSelect /* Picture size */ {0xA0, 0x00, 0x0003}, //FrameWidthHigh 00 {0xA0, 0xB0, 0x0004}, //FrameWidthLow B0 {0xA0, 0x00, 0x0005}, //FrameHeightHigh 00 {0xA0, 0x90, 0x0006}, //FrameHightLow 90 /* System */ {0xA0, 0x01, 0x0001}, //SystemOperating /* Sream and Sensor specific */ {0xA0, 0x03, 0x0012}, //VideoControlFunction {0xA0, 0x01, 0x0012}, //VideoControlFunction /* Sensor Interface */ {0xA0, 0x08, 0x008D}, //Compatibily Mode /* Window inside sensor array */ {0xA0, 0x03, 0x009A}, //WinXStartLow {0xA0, 0x00, 0x011A}, //FirstYLow {0xA0, 0x03, 0x011C}, //FirstxLow {0xA0, 0x28, 0x009C}, //WinHeightLow {0xA0, 0x68, 0x009E}, //WinWidthLow /* Init the sensor */ {0xA0, 0x02, 0x0092}, //write register 0x02 to sensor (i2c) {0xA0, 0x04, 0x0093}, //Value 0x04 {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, //end write i2c {0xA0, 0x08, 0x0092}, //write register 0x08 to sensor (i2c) {0xA0, 0x00, 0x0093}, //Value 0x00 {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, //end write i2c {0xA0, 0x09, 0x0092}, //write register 0x09 to sensor (i2c) {0xA0, 0x05, 0x0093}, //Value 0x05 {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, //end write i2c {0xA0, 0x0A, 0x0092}, //write register 0x0A to sensor (i2c) {0xA0, 0x02, 0x0093}, //Value 0x02 {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, //end write i2c {0xA0, 0x0B, 0x0092}, //write register 0x0B to sensor (i2c) {0xA0, 0x02, 0x0093}, //Value 0x02 {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, //end write i2c {0xA0, 0x0C, 0x0092}, //write register 0x0C to sensor (i2c) {0xA0, 0x05, 0x0093}, //Value 0x05 {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, //end write i2c {0xA0, 0x0D, 0x0092}, //write register 0x0D to sensor (i2c) {0xA0, 0x00, 0x0093}, //Value 0x00 {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, //end write i2c {0xA0, 0x0E, 0x0092}, //write register 0x0E to sensor (i2c) {0xA0, 0x02, 0x0093}, //Value 0x02 {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, //end write i2c {0xA0, 0x14, 0x0092}, //write register 0x14 to sensor (i2c) {0xA0, 0x81, 0x0093}, //Value 0x81 {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, //end write i2c /* Other registors */ {0xA0, 0x37, 0x0101}, //SensorCorrection /* Frame retreiving */ {0xA0, 0x00, 0x0019}, //AutoAdjustFPS /* Gains */ {0xA0, 0xa0, 0x01A8}, //DigitalGain /* Unknown */ {0xA0, 0x00, 0x01Ad}, /* Sharpness */ {0xA0, 0x03, 0x01C5}, //SharpnessMode {0xA0, 0x13, 0x01CB}, //Sharpness05 /* Other registors */ {0xA0, 0x0D, 0x0100}, //OperationMode /* Auto exposure and white balance */ {0xA0, 0x06, 0x0189}, // AWBStatus /*Dead pixels */ {0xA0, 0x08, 0x0250}, //DeadPixelsMode /* EEPROM */ {0xA0, 0x08, 0x0301}, //EEPROMAccess /* JPEG control */ {0xA0, 0x03, 0x0008}, //ClockSetting /* Unknown */ {0xA0, 0x08, 0x01C6}, /* Sharpness */ {0xA0, 0x0F, 0x01CB}, //Sharpness05 /* Other registers */ {0xA0, 0x0D, 0x0100}, //OperationMode /* Auto exposure and white balance */ {0xA0, 0x06, 0x0189}, // AWBStatus /*Dead pixels */ {0xA0, 0x08, 0x0250}, //DeadPixelsMode /* EEPROM */ {0xA0, 0x08, 0x0301}, //EEPROMAccess /* JPEG control */ {0xA0, 0x03, 0x0008}, //ClockSetting /* Sharpness */ {0xA0, 0x08, 0x01C6}, //Sharpness00 {0xA0, 0x0F, 0x01CB}, //Sharpness05 /* Color matrix */ {0xA0, 0x58, 0x010A}, {0xA0, 0xF4, 0x010B}, {0xA0, 0xF4, 0x010C}, {0xA0, 0xF4, 0x010D}, {0xA0, 0x58, 0x010E}, {0xA0, 0xF4, 0x010F}, {0xA0, 0xF4, 0x0110}, {0xA0, 0xF4, 0x0111}, {0xA0, 0x58, 0x0112}, /* Auto correction */ {0xA0, 0x03, 0x0181}, //WinXstart {0xA0, 0x08, 0x0182}, //WinXWidth {0xA0, 0x16, 0x0183}, //WinXCenter {0xA0, 0x03, 0x0184}, //WinYStart {0xA0, 0x05, 0x0185}, //WinYWidth {0xA0, 0x14, 0x0186}, //WinYCenter {0xA0, 0x00, 0x0180}, //AutoCorrectEnable /* Auto exposure and white balance */ {0xA0, 0x00, 0x0190}, //ExposureLimitHigh {0xA0, 0x03, 0x0191}, //ExposureLimitMid {0xA0, 0xB1, 0x0192}, //ExposureLimitLow {0xA0, 0x00, 0x0195}, //AntiFlickerHigh {0xA0, 0x00, 0x0196}, //AntiFlickerLow {0xA0, 0x87, 0x0197}, //AntiFlickerLow {0xA0, 0x0C, 0x018C}, //AEBFreeze {0xA0, 0x18, 0x018F}, //AEBUnfreeze /* sensor on */ {0xA0, 0x07, 0x0092}, //write register 0x07 to sensor (i2c) {0xA0, 0xB1, 0x0093}, //Value 0xB1 {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, //end write i2c {0xA0, 0x05, 0x0092}, //write register 0x05 to sensor (i2c) {0xA0, 0x03, 0x0093}, //Value 0x03 {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, //end write i2c {0xA0, 0x04, 0x0092}, //write register 0x04 to sensor (i2c) {0xA0, 0x01, 0x0093}, //Value 0x01 {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, //end write i2c {0xA0, 0x03, 0x0092}, //write register 0x03 to sensor (i2c) {0xA0, 0x3B, 0x0093}, //Value 0x3B {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, //end write i2c /* Gains */ {0xA0, 0x20, 0x01A9}, //DigitalLimitDiff {0xA0, 0x26, 0x01AA}, //DigitalGainStep {0xA0, 0xA0, 0x011D}, //GlobalGain {0xA0, 0x60, 0x011D}, //GlobalGain /* Auto correction */ {0xA0, 0x40, 0x0180}, //AutoCorrectEnable {0xa1, 0x01, 0x0180}, //AutoCorrectEnable {0xA0, 0x42, 0x0180}, //AutoCorrectEnable /* Gains */ {0xA0, 0x40, 0x0116}, //RGain {0xA0, 0x40, 0x0117}, //GGain {0xA0, 0x40, 0x0118}, //BGain {0, 0, 0} }; static __u16 pas106b_scale_data[][3] = { /* 352x288 */ /* JPEG control */ {0xA0, 0x03, 0x0008}, //ClockSetting /* Sream and Sensor specific */ {0xA0, 0x0F, 0x0010}, //CMOSSensorSelect /* Picture size */ {0xA0, 0x01, 0x0003}, //FrameWidthHigh {0xA0, 0x60, 0x0004}, //FrameWidthLow {0xA0, 0x01, 0x0005}, //FrameHeightHigh {0xA0, 0x20, 0x0006}, //FrameHightLow /* System */ {0xA0, 0x01, 0x0001}, //SystemOperating /* Sream and Sensor specific */ {0xA0, 0x03, 0x0012}, //VideoControlFunction {0xA0, 0x01, 0x0012}, //VideoControlFunction /* Sensor Interface */ {0xA0, 0x08, 0x008D}, //Compatibily Mode /* Window inside sensor array */ {0xA0, 0x03, 0x009A}, //WinXStartLow {0xA0, 0x00, 0x011A}, //FirstYLow {0xA0, 0x03, 0x011C}, //FirstxLow {0xA0, 0x28, 0x009C}, //WinHeightLow {0xA0, 0x68, 0x009E}, //WinWidthLow /* Init the sensor */ {0xA0, 0x02, 0x0092}, //write register 0x02 to sensor (i2c) {0xA0, 0x04, 0x0093}, //Value 0x04 {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, //end write i2c {0xA0, 0x08, 0x0092}, //write register 0x08 to sensor (i2c) {0xA0, 0x00, 0x0093}, //Value 0x00 {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, //end write i2c {0xA0, 0x09, 0x0092}, //write register 0x09 to sensor (i2c) {0xA0, 0x05, 0x0093}, //Value 0x05 {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, //end write i2c {0xA0, 0x0A, 0x0092}, //write register 0x0A to sensor (i2c) {0xA0, 0x02, 0x0093}, //Value 0x02 {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, //end write i2c {0xA0, 0x0B, 0x0092}, //write register 0x0B to sensor (i2c) {0xA0, 0x02, 0x0093}, //Value 0x02 {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, //end write i2c {0xA0, 0x0C, 0x0092}, //write register 0x0C to sensor (i2c) {0xA0, 0x05, 0x0093}, //Value 0x05 {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, //end write i2c {0xA0, 0x0D, 0x0092}, //write register 0x0D to sensor (i2c) {0xA0, 0x00, 0x0093}, //Value 0x00 {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, //end write i2c {0xA0, 0x0E, 0x0092}, //write register 0x0E to sensor (i2c) {0xA0, 0x02, 0x0093}, //Value 0x02 {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, //end write i2c {0xA0, 0x14, 0x0092}, //write register 0x14 to sensor (i2c) {0xA0, 0x81, 0x0093}, //Value 0x81 {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, //end write i2c /* Other registors */ {0xA0, 0x37, 0x0101}, //SensorCorrection /* Frame retreiving */ {0xA0, 0x00, 0x0019}, //AutoAdjustFPS /* Gains */ {0xA0, 0xa0, 0x01A8}, //DigitalGain /* Unknown */ {0xA0, 0x00, 0x01Ad}, /* Sharpness */ {0xA0, 0x03, 0x01C5}, //SharpnessMode {0xA0, 0x13, 0x01CB}, //Sharpness05 /* Other registors */ {0xA0, 0x0D, 0x0100}, //OperationMode /* Auto exposure and white balance */ {0xA0, 0x06, 0x0189}, // AWBStatus /*Dead pixels */ {0xA0, 0x08, 0x0250}, //DeadPixelsMode /* EEPROM */ {0xA0, 0x08, 0x0301}, //EEPROMAccess /* JPEG control */ {0xA0, 0x03, 0x0008}, //ClockSetting /* Unknown */ {0xA0, 0x08, 0x01C6}, /* Sharpness */ {0xA0, 0x0F, 0x01CB}, //Sharpness05 /* Other registers */ {0xA0, 0x0D, 0x0100}, //OperationMode /* Auto exposure and white balance */ {0xA0, 0x06, 0x0189}, // AWBStatus /*Dead pixels */ {0xA0, 0x08, 0x0250}, //DeadPixelsMode /* EEPROM */ {0xA0, 0x08, 0x0301}, //EEPROMAccess /* JPEG control */ {0xA0, 0x03, 0x0008}, //ClockSetting /* Sharpness */ {0xA0, 0x08, 0x01C6}, //Sharpness00 {0xA0, 0x0F, 0x01CB}, //Sharpness05 /* Color matrix */ {0xA0, 0x58, 0x010A}, {0xA0, 0xF4, 0x010B}, {0xA0, 0xF4, 0x010C}, {0xA0, 0xF4, 0x010D}, {0xA0, 0x58, 0x010E}, {0xA0, 0xF4, 0x010F}, {0xA0, 0xF4, 0x0110}, {0xA0, 0xF4, 0x0111}, {0xA0, 0x58, 0x0112}, /* Auto correction */ {0xA0, 0x03, 0x0181}, //WinXstart {0xA0, 0x08, 0x0182}, //WinXWidth {0xA0, 0x16, 0x0183}, //WinXCenter {0xA0, 0x03, 0x0184}, //WinYStart {0xA0, 0x05, 0x0185}, //WinYWidth {0xA0, 0x14, 0x0186}, //WinYCenter {0xA0, 0x00, 0x0180}, //AutoCorrectEnable /* Auto exposure and white balance */ {0xA0, 0x00, 0x0190}, //ExposureLimitHigh {0xA0, 0x03, 0x0191}, //ExposureLimitMid {0xA0, 0xB1, 0x0192}, //ExposureLimitLow {0xA0, 0x00, 0x0195}, //AntiFlickerHigh {0xA0, 0x00, 0x0196}, //AntiFlickerLow {0xA0, 0x87, 0x0197}, //AntiFlickerLow {0xA0, 0x0C, 0x018C}, //AEBFreeze {0xA0, 0x18, 0x018F}, //AEBUnfreeze /* sensor on */ {0xA0, 0x07, 0x0092}, //write register 0x07 to sensor (i2c) {0xA0, 0xB1, 0x0093}, //Value 0xB1 {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, //end write i2c {0xA0, 0x05, 0x0092}, //write register 0x05 to sensor (i2c) {0xA0, 0x03, 0x0093}, //Value 0x03 {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, //end write i2c {0xA0, 0x04, 0x0092}, //write register 0x04 to sensor (i2c) {0xA0, 0x01, 0x0093}, //Value 0x01 {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, //end write i2c {0xA0, 0x03, 0x0092}, //write register 0x03 to sensor (i2c) {0xA0, 0x3b, 0x0093}, //Value 0x3B {0xA0, 0x00, 0x0094}, {0xA0, 0x01, 0x0090}, {0xA1, 0x01, 0x0091}, //end write i2c /* Gains */ {0xA0, 0x20, 0x01A9}, //DigitalLimitDiff {0xA0, 0x26, 0x01AA}, //DigitalGainStep {0xA0, 0xA0, 0x011D}, //GlobalGain {0xA0, 0x60, 0x011D}, //GlobalGain /* Auto correction */ {0xA0, 0x40, 0x0180}, //AutoCorrectEnable {0xa1, 0x01, 0x0180}, //AutoCorrectEnable {0xA0, 0x42, 0x0180}, //AutoCorrectEnable /* Gains */ {0xA0, 0x40, 0x0116}, //RGain {0xA0, 0x40, 0x0117}, //GGain {0xA0, 0x40, 0x0118}, //BGain {0, 0, 0} }; pwcbsd/spca5xx-20060402/drivers/usb/pb0330.h000744 000423 000000 00000055014 10546676141 020433 0ustar00luigiwheel000000 000000 /**************************************************************************** # Photobit Pb0330 MI0360 library # # Copyright (C) 2004 Michel Xhaard mxhaard@magic.fr # # # # This program is free software; you can redistribute it and/or modify # # it under the terms of the GNU General Public License as published by # # the Free Software Foundation; either version 2 of the License, or # # (at your option) any later version. # # # # This program is distributed in the hope that it will be useful, # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # # ****************************************************************************/ /* Aurelien setting from snoop */ static __u16 pb03303x_start_data[][3] = { {0xa0, 0x01, 0x0000}, {0xa0, 0x03, 0x0008}, {0xa0, 0x0a, 0x0010}, {0xa0, 0x10, 0x0002}, {0xa0, 0x02, 0x0003}, {0xa0, 0x80, 0x0004}, {0xa0, 0x01, 0x0005}, {0xa0, 0xe0, 0x0006}, {0xa0, 0xdc, 0x008b}, //8b -> dc {0xa0, 0x01, 0x0001}, {0xa0, 0x03, 0x0012}, {0xa0, 0x01, 0x0012}, {0xa0, 0x00, 0x0098}, {0xa0, 0x00, 0x009a}, {0xa0, 0x00, 0x011a}, {0xa0, 0x00, 0x011c}, {0xa0, 0xdc, 0x008b}, {0xa0, 0x01, 0x0092}, {0xa0, 0x01, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x06, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x08, 0x0092}, {0xa0, 0x83, 0x0093}, {0xa0, 0x04, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x01, 0x0092}, {0xa0, 0x04, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x08, 0x0092}, {0xa0, 0x06, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x02, 0x0092}, {0xa0, 0x11, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x03, 0x0092}, {0xa0, 0xe7, 0x0093}, {0xa0, 0x01, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x04, 0x0092}, {0xa0, 0x87, 0x0093}, {0xa0, 0x02, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x07, 0x0092}, {0xa0, 0x02, 0x0093}, {0xa0, 0x30, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x20, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x11, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x35, 0x0092}, {0xa0, 0x50, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x30, 0x0092}, {0xa0, 0x05, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x31, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x58, 0x0092}, {0xa0, 0x78, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x62, 0x0092}, {0xa0, 0x11, 0x0093}, {0xa0, 0x04, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x2b, 0x0092}, {0xa0, 0x28, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x2c, 0x0092}, {0xa0, 0x30, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x2d, 0x0092}, {0xa0, 0x30, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x2e, 0x0092}, {0xa0, 0x28, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x10, 0x0087}, {0xa0, 0x37, 0x0101}, {0xa0, 0x05, 0x0012}, {0xa0, 0x0d, 0x0100}, {0xa0, 0x06, 0x0189}, {0xa0, 0x00, 0x01ad}, {0xa0, 0x03, 0x01c5}, {0xa0, 0x13, 0x01cb}, {0xa0, 0x08, 0x0250}, {0xa0, 0x08, 0x0301}, {0xa0, 0x60, 0x01a8}, {0xa0, 0x78, 0x018d}, {0xa0, 0x61, 0x0116}, {0xa0, 0x65, 0x0118}, {0xa1, 0x01, 0x0002}, {0xa0, 0x09, 0x01ad}, {0xa0, 0x15, 0x01ae}, {0xa0, 0x0d, 0x003a}, {0xa0, 0x02, 0x003b}, {0xa0, 0x00, 0x0038}, {0xa0, 0x50, 0x010a}, {0xa0, 0xf8, 0x010b}, {0xa0, 0xf8, 0x010c}, {0xa0, 0xf8, 0x010d}, {0xa0, 0x50, 0x010e}, {0xa0, 0xf8, 0x010f}, {0xa0, 0xf8, 0x0110}, {0xa0, 0xf8, 0x0111}, {0xa0, 0x50, 0x0112}, {0xa1, 0x01, 0x0008}, {0xa0, 0x03, 0x0008}, {0xa0, 0x08, 0x01c6}, {0xa1, 0x01, 0x01c8}, {0xa1, 0x01, 0x01c9}, {0xa1, 0x01, 0x01ca}, {0xa0, 0x0f, 0x01cb}, {0xa0, 0x13, 0x0120}, {0xa0, 0x38, 0x0121}, {0xa0, 0x59, 0x0122}, {0xa0, 0x79, 0x0123}, {0xa0, 0x92, 0x0124}, {0xa0, 0xa7, 0x0125}, {0xa0, 0xb9, 0x0126}, {0xa0, 0xc8, 0x0127}, {0xa0, 0xd4, 0x0128}, {0xa0, 0xdf, 0x0129}, {0xa0, 0xe7, 0x012a}, {0xa0, 0xee, 0x012b}, {0xa0, 0xf4, 0x012c}, {0xa0, 0xf9, 0x012d}, {0xa0, 0xfc, 0x012e}, {0xa0, 0xff, 0x012f}, {0xa0, 0x26, 0x0130}, {0xa0, 0x22, 0x0131}, {0xa0, 0x20, 0x0132}, {0xa0, 0x1c, 0x0133}, {0xa0, 0x16, 0x0134}, {0xa0, 0x13, 0x0135}, {0xa0, 0x10, 0x0136}, {0xa0, 0x0d, 0x0137}, {0xa0, 0x0b, 0x0138}, {0xa0, 0x09, 0x0139}, {0xa0, 0x07, 0x013a}, {0xa0, 0x06, 0x013b}, {0xa0, 0x05, 0x013c}, {0xa0, 0x04, 0x013d}, {0xa0, 0x03, 0x013e}, {0xa0, 0x02, 0x013f}, {0xa0, 0x50, 0x010a}, {0xa0, 0xf8, 0x010b}, {0xa0, 0xf8, 0x010c}, {0xa0, 0xf8, 0x010d}, {0xa0, 0x50, 0x010e}, {0xa0, 0xf8, 0x010f}, {0xa0, 0xf8, 0x0110}, {0xa0, 0xf8, 0x0111}, {0xa0, 0x50, 0x0112}, {0xa1, 0x01, 0x0180}, {0xa0, 0x00, 0x0180}, {0xa0, 0x00, 0x0180}, {0xa0, 0x00, 0x0019}, {0xa0, 0x05, 0x0092}, {0xa0, 0x09, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x09, 0x0092}, {0xa0, 0x34, 0x0093}, {0xa0, 0x01, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x00, 0x0190}, {0xa0, 0x07, 0x0191}, {0xa0, 0xec, 0x0192}, {0xa0, 0x00, 0x0195}, {0xa0, 0x00, 0x0196}, {0xa0, 0x9c, 0x0197}, {0xa0, 0x0e, 0x018c}, {0xa0, 0x1c, 0x018f}, {0xa0, 0x14, 0x01a9}, {0xa0, 0x24, 0x01aa}, {0xa0, 0xd7, 0x001d}, {0xa0, 0xf4, 0x001e}, {0xa0, 0xf9, 0x001f}, {0xa0, 0xff, 0x0020}, {0xa0, 0x42, 0x0180}, {0xa0, 0x09, 0x01ad}, {0xa0, 0x15, 0x01ae}, {0xa0, 0x40, 0x0180}, {0xa1, 0x01, 0x0180}, {0xa0, 0x42, 0x0180}, {0, 0, 0} }; static __u16 pb03303x_scale_data[][3] = { {0xa0, 0x01, 0x0000}, {0xa0, 0x03, 0x0008}, {0xa0, 0x0a, 0x0010}, {0xa0, 0x00, 0x0002}, {0xa0, 0x02, 0x0003}, {0xa0, 0x80, 0x0004}, {0xa0, 0x01, 0x0005}, {0xa0, 0xe0, 0x0006}, {0xa0, 0xdc, 0x008b}, //8b -> dc {0xa0, 0x01, 0x0001}, {0xa0, 0x03, 0x0012}, {0xa0, 0x01, 0x0012}, {0xa0, 0x00, 0x0098}, {0xa0, 0x00, 0x009a}, {0xa0, 0x00, 0x011a}, {0xa0, 0x00, 0x011c}, {0xa0, 0xdc, 0x008b}, {0xa0, 0x01, 0x0092}, {0xa0, 0x01, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x06, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x08, 0x0092}, {0xa0, 0x83, 0x0093}, {0xa0, 0x04, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x01, 0x0092}, {0xa0, 0x04, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x08, 0x0092}, {0xa0, 0x06, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x02, 0x0092}, {0xa0, 0x11, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x03, 0x0092}, {0xa0, 0xe7, 0x0093}, {0xa0, 0x01, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x04, 0x0092}, {0xa0, 0x87, 0x0093}, {0xa0, 0x02, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x07, 0x0092}, {0xa0, 0x02, 0x0093}, {0xa0, 0x30, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x20, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x11, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x35, 0x0092}, {0xa0, 0x50, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x30, 0x0092}, {0xa0, 0x05, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x31, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x58, 0x0092}, {0xa0, 0x78, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x62, 0x0092}, {0xa0, 0x11, 0x0093}, {0xa0, 0x04, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x2b, 0x0092}, {0xa0, 0x28, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x2c, 0x0092}, {0xa0, 0x30, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x2d, 0x0092}, {0xa0, 0x30, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x2e, 0x0092}, {0xa0, 0x28, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x10, 0x0087}, {0xa0, 0x37, 0x0101}, {0xa0, 0x05, 0x0012}, {0xa0, 0x0d, 0x0100}, {0xa0, 0x06, 0x0189}, {0xa0, 0x00, 0x01ad}, {0xa0, 0x03, 0x01c5}, {0xa0, 0x13, 0x01cb}, {0xa0, 0x08, 0x0250}, {0xa0, 0x08, 0x0301}, {0xa0, 0x60, 0x01a8}, {0xa0, 0x78, 0x018d}, {0xa0, 0x61, 0x0116}, {0xa0, 0x65, 0x0118}, {0xa1, 0x01, 0x0002}, {0xa0, 0x09, 0x01ad}, {0xa0, 0x15, 0x01ae}, {0xa0, 0x0d, 0x003a}, {0xa0, 0x02, 0x003b}, {0xa0, 0x00, 0x0038}, {0xa0, 0x50, 0x010a}, {0xa0, 0xf8, 0x010b}, {0xa0, 0xf8, 0x010c}, {0xa0, 0xf8, 0x010d}, {0xa0, 0x50, 0x010e}, {0xa0, 0xf8, 0x010f}, {0xa0, 0xf8, 0x0110}, {0xa0, 0xf8, 0x0111}, {0xa0, 0x50, 0x0112}, {0xa1, 0x01, 0x0008}, {0xa0, 0x03, 0x0008}, {0xa0, 0x08, 0x01c6}, {0xa1, 0x01, 0x01c8}, {0xa1, 0x01, 0x01c9}, {0xa1, 0x01, 0x01ca}, {0xa0, 0x0f, 0x01cb}, {0xa0, 0x13, 0x0120}, {0xa0, 0x38, 0x0121}, {0xa0, 0x59, 0x0122}, {0xa0, 0x79, 0x0123}, {0xa0, 0x92, 0x0124}, {0xa0, 0xa7, 0x0125}, {0xa0, 0xb9, 0x0126}, {0xa0, 0xc8, 0x0127}, {0xa0, 0xd4, 0x0128}, {0xa0, 0xdf, 0x0129}, {0xa0, 0xe7, 0x012a}, {0xa0, 0xee, 0x012b}, {0xa0, 0xf4, 0x012c}, {0xa0, 0xf9, 0x012d}, {0xa0, 0xfc, 0x012e}, {0xa0, 0xff, 0x012f}, {0xa0, 0x26, 0x0130}, {0xa0, 0x22, 0x0131}, {0xa0, 0x20, 0x0132}, {0xa0, 0x1c, 0x0133}, {0xa0, 0x16, 0x0134}, {0xa0, 0x13, 0x0135}, {0xa0, 0x10, 0x0136}, {0xa0, 0x0d, 0x0137}, {0xa0, 0x0b, 0x0138}, {0xa0, 0x09, 0x0139}, {0xa0, 0x07, 0x013a}, {0xa0, 0x06, 0x013b}, {0xa0, 0x05, 0x013c}, {0xa0, 0x04, 0x013d}, {0xa0, 0x03, 0x013e}, {0xa0, 0x02, 0x013f}, {0xa0, 0x50, 0x010a}, {0xa0, 0xf8, 0x010b}, {0xa0, 0xf8, 0x010c}, {0xa0, 0xf8, 0x010d}, {0xa0, 0x50, 0x010e}, {0xa0, 0xf8, 0x010f}, {0xa0, 0xf8, 0x0110}, {0xa0, 0xf8, 0x0111}, {0xa0, 0x50, 0x0112}, {0xa1, 0x01, 0x0180}, {0xa0, 0x00, 0x0180}, {0xa0, 0x00, 0x0180}, {0xa0, 0x00, 0x0019}, {0xa0, 0x05, 0x0092}, {0xa0, 0x09, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x09, 0x0092}, {0xa0, 0x34, 0x0093}, {0xa0, 0x01, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x00, 0x0190}, {0xa0, 0x07, 0x0191}, {0xa0, 0xec, 0x0192}, {0xa0, 0x00, 0x0195}, {0xa0, 0x00, 0x0196}, {0xa0, 0x9c, 0x0197}, {0xa0, 0x0e, 0x018c}, {0xa0, 0x1c, 0x018f}, {0xa0, 0x14, 0x01a9}, {0xa0, 0x24, 0x01aa}, {0xa0, 0xd7, 0x001d}, {0xa0, 0xf4, 0x001e}, {0xa0, 0xf9, 0x001f}, {0xa0, 0xff, 0x0020}, {0xa0, 0x42, 0x0180}, {0xa0, 0x09, 0x01ad}, {0xa0, 0x15, 0x01ae}, {0xa0, 0x40, 0x0180}, {0xa1, 0x01, 0x0180}, {0xa0, 0x42, 0x0180}, {0, 0, 0} }; static __u16 pb0330xx_start_data[][3] = { {0xa1, 0x01, 0x0008}, {0xa1, 0x01, 0x0008}, {0xa0, 0x01, 0x0000}, {0xa0, 0x03, 0x0008}, //00 {0xa0, 0x0a, 0x0010}, {0xa0, 0x10, 0x0002}, {0xa0, 0x02, 0x0003}, {0xa0, 0x80, 0x0004}, {0xa0, 0x01, 0x0005}, {0xa0, 0xe0, 0x0006}, {0xa0, 0x01, 0x0001}, {0xa0, 0x05, 0x0012}, {0xa0, 0x07, 0x0012}, {0xa0, 0x00, 0x0098}, {0xa0, 0x00, 0x009a}, {0xa0, 0x00, 0x011a}, {0xa0, 0x00, 0x011c}, {0xa0, 0x05, 0x0012}, {0xa0, 0x01, 0x0092}, {0xa0, 0x06, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x02, 0x0092}, {0xa0, 0x11, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x03, 0x0092}, {0xa0, 0xe7, 0x0093}, {0xa0, 0x01, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x04, 0x0092}, {0xa0, 0x87, 0x0093}, {0xa0, 0x02, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x06, 0x0092}, {0xa0, 0x03, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x07, 0x0092}, {0xa0, 0x02, 0x0093}, {0xa0, 0x30, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x20, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x11, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x2f, 0x0092}, {0xa0, 0xb0, 0x0093}, {0xa0, 0xf7, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x30, 0x0092}, {0xa0, 0x05, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x31, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x34, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x01, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x35, 0x0092}, {0xa0, 0x60, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x3d, 0x0092}, {0xa0, 0x8f, 0x0093}, {0xa0, 0x06, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x40, 0x0092}, {0xa0, 0xe0, 0x0093}, {0xa0, 0x01, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x58, 0x0092}, {0xa0, 0x78, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x62, 0x0092}, {0xa0, 0x11, 0x0093}, {0xa0, 0x04, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x10, 0x0087}, {0xa0, 0x37, 0x0101}, {0xa0, 0x05, 0x0012}, {0xa0, 0x0d, 0x0100}, {0xa0, 0x06, 0x0189}, {0xa0, 0x00, 0x01ad}, {0xa0, 0x03, 0x01c5}, {0xa0, 0x13, 0x01cb}, {0xa0, 0x08, 0x0250}, {0xa0, 0x08, 0x0301}, {0xa0, 0x60, 0x01a8}, {0xa0, 0x6c, 0x018d}, {0xa1, 0x01, 0x0002}, {0xa0, 0x09, 0x01ad}, {0xa0, 0x15, 0x01ae}, {0xa0, 0x00, 0x0092}, {0xa0, 0x02, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa1, 0x01, 0x0095}, {0xa1, 0x01, 0x0096}, {0xa0, 0x50, 0x010a}, {0xa0, 0xf8, 0x010b}, {0xa0, 0xf8, 0x010c}, {0xa0, 0xf8, 0x010d}, {0xa0, 0x50, 0x010e}, {0xa0, 0xf8, 0x010f}, {0xa0, 0xf8, 0x0110}, {0xa0, 0xf8, 0x0111}, {0xa0, 0x50, 0x0112}, {0xa1, 0x01, 0x0008}, {0xa0, 0x03, 0x0008}, //00 {0xa0, 0x08, 0x01c6}, {0xa1, 0x01, 0x01c8}, {0xa1, 0x01, 0x01c9}, {0xa1, 0x01, 0x01ca}, {0xa0, 0x0f, 0x01cb}, {0xa0, 0x50, 0x010a}, {0xa0, 0xf8, 0x010b}, {0xa0, 0xf8, 0x010c}, {0xa0, 0xf8, 0x010d}, {0xa0, 0x50, 0x010e}, {0xa0, 0xf8, 0x010f}, {0xa0, 0xf8, 0x0110}, {0xa0, 0xf8, 0x0111}, {0xa0, 0x50, 0x0112}, {0xa1, 0x01, 0x0180}, {0xa0, 0x00, 0x0180}, {0xa0, 0x00, 0x0019}, {0xa0, 0x05, 0x0092}, {0xa0, 0x66, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x09, 0x0092}, {0xa0, 0xb2, 0x0093}, {0xa0, 0x02, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x10, 0x0092}, {0xa0, 0x02, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x60, 0x011d}, {0xa0, 0x00, 0x0190}, {0xa0, 0x07, 0x0191}, {0xa0, 0x8c, 0x0192}, {0xa0, 0x00, 0x0195}, {0xa0, 0x00, 0x0196}, {0xa0, 0x8a, 0x0197}, {0xa0, 0x10, 0x018c}, {0xa0, 0x20, 0x018f}, {0xa0, 0x14, 0x01a9}, {0xa0, 0x24, 0x01aa}, {0xa0, 0xd7, 0x001d}, {0xa0, 0xf0, 0x001e}, {0xa0, 0xf8, 0x001f}, {0xa0, 0xff, 0x0020}, {0xa0, 0x09, 0x01ad}, {0xa0, 0x15, 0x01ae}, {0xa0, 0x40, 0x0180}, {0xa1, 0x01, 0x0180}, {0xa0, 0x42, 0x0180}, {0xa1, 0x01, 0x0008}, {0xa1, 0x01, 0x0007}, // { 0xa0, 0x30, 0x0007}, // { 0xa0, 0x00, 0x0007}, {0, 0, 0} }; static __u16 pb0330xx_scale_data[][3] = { {0xa1, 0x01, 0x0008}, {0xa1, 0x01, 0x0008}, {0xa0, 0x01, 0x0000}, {0xa0, 0x03, 0x0008}, //00 {0xa0, 0x0a, 0x0010}, {0xa0, 0x00, 0x0002}, // 10 {0xa0, 0x02, 0x0003}, {0xa0, 0x80, 0x0004}, {0xa0, 0x01, 0x0005}, {0xa0, 0xe0, 0x0006}, {0xa0, 0x01, 0x0001}, {0xa0, 0x05, 0x0012}, {0xa0, 0x07, 0x0012}, {0xa0, 0x00, 0x0098}, {0xa0, 0x00, 0x009a}, {0xa0, 0x00, 0x011a}, {0xa0, 0x00, 0x011c}, {0xa0, 0x05, 0x0012}, {0xa0, 0x01, 0x0092}, {0xa0, 0x06, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x02, 0x0092}, {0xa0, 0x11, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x03, 0x0092}, {0xa0, 0xe7, 0x0093}, {0xa0, 0x01, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x04, 0x0092}, {0xa0, 0x87, 0x0093}, {0xa0, 0x02, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x06, 0x0092}, {0xa0, 0x03, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x07, 0x0092}, {0xa0, 0x02, 0x0093}, {0xa0, 0x30, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x20, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x11, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x2f, 0x0092}, {0xa0, 0xb0, 0x0093}, {0xa0, 0xf7, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x30, 0x0092}, {0xa0, 0x05, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x31, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x34, 0x0092}, {0xa0, 0x00, 0x0093}, {0xa0, 0x01, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x35, 0x0092}, {0xa0, 0x60, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x3d, 0x0092}, {0xa0, 0x8f, 0x0093}, {0xa0, 0x06, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x40, 0x0092}, {0xa0, 0xe0, 0x0093}, {0xa0, 0x01, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x58, 0x0092}, {0xa0, 0x78, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x62, 0x0092}, {0xa0, 0x11, 0x0093}, {0xa0, 0x04, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x10, 0x0087}, {0xa0, 0x37, 0x0101}, {0xa0, 0x05, 0x0012}, {0xa0, 0x0d, 0x0100}, {0xa0, 0x06, 0x0189}, {0xa0, 0x00, 0x01ad}, {0xa0, 0x03, 0x01c5}, {0xa0, 0x13, 0x01cb}, {0xa0, 0x08, 0x0250}, {0xa0, 0x08, 0x0301}, {0xa0, 0x60, 0x01a8}, {0xa0, 0x6c, 0x018d}, {0xa1, 0x01, 0x0002}, {0xa0, 0x09, 0x01ad}, {0xa0, 0x15, 0x01ae}, {0xa0, 0x00, 0x0092}, {0xa0, 0x02, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa1, 0x01, 0x0095}, {0xa1, 0x01, 0x0096}, {0xa0, 0x50, 0x010a}, {0xa0, 0xf8, 0x010b}, {0xa0, 0xf8, 0x010c}, {0xa0, 0xf8, 0x010d}, {0xa0, 0x50, 0x010e}, {0xa0, 0xf8, 0x010f}, {0xa0, 0xf8, 0x0110}, {0xa0, 0xf8, 0x0111}, {0xa0, 0x50, 0x0112}, {0xa1, 0x01, 0x0008}, {0xa0, 0x03, 0x0008}, //00 {0xa0, 0x08, 0x01c6}, {0xa1, 0x01, 0x01c8}, {0xa1, 0x01, 0x01c9}, {0xa1, 0x01, 0x01ca}, {0xa0, 0x0f, 0x01cb}, {0xa0, 0x50, 0x010a}, {0xa0, 0xf8, 0x010b}, {0xa0, 0xf8, 0x010c}, {0xa0, 0xf8, 0x010d}, {0xa0, 0x50, 0x010e}, {0xa0, 0xf8, 0x010f}, {0xa0, 0xf8, 0x0110}, {0xa0, 0xf8, 0x0111}, {0xa0, 0x50, 0x0112}, {0xa1, 0x01, 0x0180}, {0xa0, 0x00, 0x0180}, {0xa0, 0x00, 0x0019}, {0xa0, 0x05, 0x0092}, {0xa0, 0x66, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x09, 0x0092}, {0xa0, 0xb2, 0x0093}, {0xa0, 0x02, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x10, 0x0092}, {0xa0, 0x02, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x60, 0x011d}, {0xa0, 0x00, 0x0190}, {0xa0, 0x07, 0x0191}, {0xa0, 0x8c, 0x0192}, {0xa0, 0x00, 0x0195}, {0xa0, 0x00, 0x0196}, {0xa0, 0x8a, 0x0197}, {0xa0, 0x10, 0x018c}, {0xa0, 0x20, 0x018f}, {0xa0, 0x14, 0x01a9}, {0xa0, 0x24, 0x01aa}, {0xa0, 0xd7, 0x001d}, {0xa0, 0xf0, 0x001e}, {0xa0, 0xf8, 0x001f}, {0xa0, 0xff, 0x0020}, {0xa0, 0x09, 0x01ad}, {0xa0, 0x15, 0x01ae}, {0xa0, 0x40, 0x0180}, {0xa1, 0x01, 0x0180}, {0xa0, 0x42, 0x0180}, {0xa1, 0x01, 0x0008}, {0xa1, 0x01, 0x0007}, // { 0xa0, 0x30, 0x0007}, // { 0xa0, 0x00, 0x0007}, {0, 0, 0} }; pwcbsd/spca5xx-20060402/drivers/usb/sn9cxxx.h000755 000423 000000 00000100520 10553461760 021140 0ustar00luigiwheel000000 000000 #ifndef SONIXJPGUSB_H #define SONIXJPGUSB_H /**************************************************************************** # Sonix sn9c102p sn9c105 sn9c120 library # # Copyright (C) 2005 Michel Xhaard mxhaard@magic.fr # # # # This program is free software; you can redistribute it and/or modify # # it under the terms of the GNU General Public License as published by # # the Free Software Foundation; either version 2 of the License, or # # (at your option) any later version. # # # # This program is distributed in the hope that it will be useful, # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # # ****************************************************************************/ static int sn9cxxx_init(struct usb_spca50x *spca50x); static void sn9cxxx_start(struct usb_spca50x *spca50x); static void sn9cxxx_stop(struct usb_spca50x *spca50x); static __u16 sn9cxxx_setbrightness(struct usb_spca50x *spca50x); static __u16 sn9cxxx_getbrightness(struct usb_spca50x *spca50x); static __u16 sn9cxxx_setcontrast(struct usb_spca50x *spca50x); static __u16 sn9cxxx_getcontrast(struct usb_spca50x *spca50x); static unsigned int sn9cxxx_getexposure(struct usb_spca50x *spca50x); //static void sn9cxxx_setAutobright (struct usb_spca50x *spca50x); //static void sn9cxxx_shutdown(struct usb_spca50x *spca50x); static int sn9cxxx_probesensor(struct usb_spca50x *spca50x); enum { SN9C101 = 0, SN9C102, SN9C102P, SN9C103, SN9C105, SN9C120, }; static __u8 sn_mi0360[] = { 0x00, 0x61, 0x44, 0x00, 0x1A, 0x20, 0x20, 0x20, 0xB1, 0x5D, 0x07, 0x00, 0x00, 0x00, 0x00, 0x10, 0x03, 0x00, //reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 reg9 rega regb regc regd rege regf reg10 reg11 0x00, 0x02, 0x0a, 0x28, 0x1e, 0x61, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 //reg12 reg13 reg14 reg15 reg16 reg17 reg18 reg19 reg1a reg1b reg1c reg1d reg1e reg1f reg20 reg21 reg22 reg23 }; /*Data from sn9c102p+hv71331r */ static __u8 sn_hv7131[] = { 0x00, 0x03, 0x64, 0x00, 0x1A, 0x20, 0x20, 0x20, 0xA1, 0x11, 0x02, 0x09, 0x00, 0x00, 0x00, 0x10, 0x03, 0x00, //00 //reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 reg9 rega regb regc regd rege regf reg10 reg11 0x00, 0x01, 0x03, 0x28, 0x1e, 0x41, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 //reg12 reg13 reg14 reg15 reg16 reg17 reg18 reg19 reg1a reg1b reg1c reg1d reg1e reg1f reg20 reg21 reg22 reg23 }; static __u8 reg9a[] = { 0x08, 0x40, 0x20, 0x10, 0x00, 0x04 }; static __u8 regsn20[] = { 0x00, 0x2D, 0x46, 0x5A, 0x6C, 0x7C, 0x8B, 0x99, 0xA6, 0xB2, 0xBF, 0xCA, 0xD5, 0xE0, 0xEB, 0xF5, 0xFF }; static __u8 reg84[] = { 0x14, 0x00, 0x27, 0x00, 0x07, 0x00, 0xE5, 0x0F, 0xE4, 0x0F, 0x38, 0x00, 0x3E, 0x00, 0xC3, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00 }; static __u8 hv7131r_sensor_init[][8] = { {0xC1, 0x11, 0x01, 0x08, 0x01, 0x00, 0x00, 0x10}, {0xB1, 0x11, 0x34, 0x17, 0x7F, 0x00, 0x00, 0x10}, {0xD1, 0x11, 0x40, 0xFF, 0x7F, 0x7F, 0x7F, 0x10}, {0x91, 0x11, 0x44, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xD1, 0x11, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xD1, 0x11, 0x14, 0x01, 0xE2, 0x02, 0x82, 0x10}, {0x91, 0x11, 0x18, 0x00, 0x00, 0x00, 0x00, 0x10}, // {0xA1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10}, {0xA1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10}, {0xC1, 0x11, 0x25, 0x00, 0x61, 0xA8, 0x00, 0x10}, {0xA1, 0x11, 0x30, 0x22, 0x00, 0x00, 0x00, 0x10}, {0xC1, 0x11, 0x31, 0x20, 0x2E, 0x20, 0x00, 0x10}, {0xC1, 0x11, 0x25, 0x00, 0xC3, 0x50, 0x00, 0x10}, {0xA1, 0x11, 0x30, 0x07, 0x00, 0x00, 0x00, 0x10}, //gain14 {0xC1, 0x11, 0x31, 0x10, 0x10, 0x10, 0x00, 0x10}, //r g b 101a10 // {0xA1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10}, {0xA1, 0x11, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xA1, 0x11, 0x21, 0xD0, 0x00, 0x00, 0x00, 0x10}, {0xA1, 0x11, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xA1, 0x11, 0x23, 0x09, 0x00, 0x00, 0x00, 0x10}, // {0xA1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10}, {0xA1, 0x11, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xA1, 0x11, 0x21, 0xD0, 0x00, 0x00, 0x00, 0x10}, {0xA1, 0x11, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xA1, 0x11, 0x23, 0x10, 0x00, 0x00, 0x00, 0x10}, {0, 0, 0, 0, 0, 0, 0, 0} }; static __u8 mi0360_sensor_init[][8] = { {0xB1, 0x5D, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10}, {0xB1, 0x5D, 0x0D, 0x00, 0x01, 0x00, 0x00, 0x10}, {0xB1, 0x5D, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xD1, 0x5D, 0x01, 0x00, 0x08, 0x00, 0x16, 0x10}, {0xD1, 0x5D, 0x03, 0x01, 0xE2, 0x02, 0x82, 0x10}, {0xD1, 0x5D, 0x05, 0x00, 0x09, 0x00, 0x53, 0x10}, {0xB1, 0x5D, 0x0D, 0x00, 0x02, 0x00, 0x00, 0x10}, {0xD1, 0x5D, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xD1, 0x5D, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xD1, 0x5D, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xD1, 0x5D, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xD1, 0x5D, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xD1, 0x5D, 0x14, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xD1, 0x5D, 0x16, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xD1, 0x5D, 0x18, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xD1, 0x5D, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xD1, 0x5D, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xB1, 0x5D, 0x32, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xD1, 0x5D, 0x20, 0x91, 0x01, 0x00, 0x00, 0x10}, {0xD1, 0x5D, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xD1, 0x5D, 0x24, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xD1, 0x5D, 0x26, 0x00, 0x00, 0x00, 0x24, 0x10}, {0xD1, 0x5D, 0x2F, 0xF7, 0xB0, 0x00, 0x04, 0x10}, {0xD1, 0x5D, 0x31, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xD1, 0x5D, 0x33, 0x00, 0x00, 0x01, 0x00, 0x10}, {0xB1, 0x5D, 0x3D, 0x06, 0x8F, 0x00, 0x00, 0x10}, {0xD1, 0x5D, 0x40, 0x01, 0xE0, 0x00, 0xD1, 0x10}, {0xB1, 0x5D, 0x44, 0x00, 0x82, 0x00, 0x00, 0x10}, {0xD1, 0x5D, 0x58, 0x00, 0x78, 0x00, 0x43, 0x10}, {0xD1, 0x5D, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xD1, 0x5D, 0x5C, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xD1, 0x5D, 0x5E, 0x00, 0x00, 0xA3, 0x1D, 0x10}, {0xB1, 0x5D, 0x62, 0x04, 0x11, 0x00, 0x00, 0x10}, // {0xB1, 0x5D, 0x64, 0x00, 0x3F, 0x00, 0x00, 0x10}, //blue offset ??? {0xB1, 0x5D, 0x20, 0x91, 0x01, 0x00, 0x00, 0x10}, {0xB1, 0x5D, 0x20, 0x11, 0x01, 0x00, 0x00, 0x10}, {0xB1, 0x5D, 0x09, 0x00, 0x64, 0x00, 0x00, 0x10}, {0xD1, 0x5D, 0x2B, 0x00, 0xA0, 0x00, 0xB0, 0x10}, {0xD1, 0x5D, 0x2D, 0x00, 0xA0, 0x00, 0xA0, 0x10}, {0xB1, 0x5D, 0x0A, 0x00, 0x02, 0x00, 0x00, 0x10}, //sensor clck ?2 {0xB1, 0x5D, 0x06, 0x00, 0x30, 0x00, 0x00, 0x10}, {0xB1, 0x5D, 0x05, 0x00, 0x0A, 0x00, 0x00, 0x10}, {0xB1, 0x5D, 0x09, 0x02, 0x35, 0x00, 0x00, 0x10}, // exposure 2 //{ 0xD1,0x5D,0x2B,0x00,0xB9,0x00,0xE3,0x10 }, //{ 0xD1,0x5D,0x2D,0x00,0xC5,0x00,0xB9,0x10 }, {0xB1, 0x5D, 0x35, 0x00, 0x67, 0x00, 0x00, 0x10}, //gain {0xB1, 0x5D, 0x07, 0x00, 0x03, 0x00, 0x00, 0x10}, //update {0xB1, 0x5D, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10}, //sensor on {0, 0, 0, 0, 0, 0, 0, 0} }; #if 0 /* Data from sn9c120+hv7131r */ static __u8 hv7131r_sensor_init[][8] = { {0xc1, 0x11, 0x01, 0x08, 0x01, 0x00, 0x00, 0x10}, {0xd1, 0x11, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xd1, 0x11, 0x14, 0x01, 0xe2, 0x02, 0x82, 0x10}, {0xb1, 0x11, 0x34, 0x17, 0x7f, 0x00, 0x00, 0x10}, {0xd1, 0x11, 0x40, 0xff, 0x7f, 0x7f, 0x7f, 0x10}, {0xa1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10}, {0xc1, 0x11, 0x25, 0x00, 0xc3, 0x50, 0x00, 0x10}, {0xa1, 0x11, 0x30, 0x20, 0x00, 0x00, 0x00, 0x10}, {0xc1, 0x11, 0x31, 0x13, 0x06, 0x12, 0x00, 0x10}, {0xa1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x11, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x11, 0x21, 0xd0, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x11, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x11, 0x23, 0x08, 0x00, 0x00, 0x00, 0x10}, {0xc1, 0x11, 0x31, 0x17, 0x06, 0x10, 0x00, 0x10}, {0xa1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x11, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x11, 0x21, 0xd1, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x11, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x11, 0x23, 0x10, 0x00, 0x00, 0x00, 0x10}, {0, 0, 0, 0, 0, 0, 0, 0} }; static __u8 sn_hv7131[] = { 0x00, 0x23, 0x60, 0x00, 0x1A, 0x1f, 0x21, 0x1f, 0xA1, 0x11, 0x02, 0x09, 0x00, 0x00, 0x00, 0x10, 0x03, 0x00, //reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 reg9 rega regb regc regd rege regf reg10 reg11 0x00, 0x01, 0x0c, 0x28, 0x1e, 0x60, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 //reg12 reg13 reg14 reg15 reg16 reg17 reg18 reg19 reg1a reg1b reg1c reg1d reg1e reg1f reg20 reg21 reg22 reg23 }; static __u8 reg9a[] = { //HV7131//0x08, 0x40, 0x20, 0x10, 0x00, 0x04 //0x00, 0x40, 0x20, 0x00, 0x00, 0x00 0x07, 0x40, 0x20, 0x00, 0x00, 0x00 //MI0360 }; static __u8 regsn20[] = { //HV7131//0x00, 0x2D, 0x46, 0x5A, 0x6C, 0x7C, 0x8B, 0x99, 0xA6, 0xB2, 0xBF, 0xCA, 0xD5, 0xE0, 0xEB, 0xF5, 0xFF //0x08, 0x3a, 0x52, 0x65, 0x75, 0x83, 0x91, 0x9d, 0xa9, 0xb4, 0xbe, 0xc8, 0xd2, 0xdb, 0xe4, 0xed, 0xf5 //hv7131R 0x08, 0x39, 0x51, 0x63, 0x73, 0x82, 0x8f, 0x9b, 0xa7, 0xb1, 0xbc, 0xc6, 0xcf, 0xd8, 0xe1, 0xea, 0xf2 //mi0360 }; static __u8 reg84[] = { //HV7131//0x14, 0x00, 0x27, 0x00, 0x07, 0x00, 0xE5, 0x0F, 0xE4, 0x0F, 0x38, 0x00, 0x3E, 0x00, 0xC3, 0x0F, //0x00, 0x00, 0x00, 0x00, 0x00 //HV7131//0x13, 0x00, 0x25, 0x00, 0x07, 0x00, 0xef, 0x0f, 0xdf, 0x0f, 0x33, 0x00, 0x38, 0x00, 0xd1, 0x0f, //0xf7, 0x0f, 0x0f, 0x00, 0x00 //MI0360 0x13, 0x00, 0x25, 0x00, 0x07, 0x00, 0xee, 0x0f, 0xe5, 0x0f, 0x2e, 0x00, 0x30, 0x00, 0xd4, 0x0f, 0xfc, 0x0f, 0x14, 0x00, 0x00 }; #endif #if 0 static __u8 qtable1[] = { 0x0B, 0x07, 0x07, 0x0B, 0x07, 0x07, 0x0B, 0x0B, 0x0B, 0x0B, 0x0E, 0x0B, 0x0B, 0x0E, 0x12, 0x1D, 0x12, 0x12, 0x0E, 0x0E, 0x12, 0x24, 0x19, 0x19, 0x15, 0x1D, 0x2B, 0x24, 0x2B, 0x2B, 0x27, 0x24, 0x27, 0x27, 0x2F, 0x32, 0x40, 0x39, 0x2F, 0x32, 0x3D, 0x32, 0x27, 0x27, 0x39, 0x4F, 0x39, 0x3D, 0x44, 0x48, 0x4B, 0x4B, 0x4B, 0x2B, 0x36, 0x52, 0x56, 0x4F, 0x48, 0x56, 0x40, 0x48, 0x4B, 0x48, 0x0B, 0x0E, 0x0E, 0x12, 0x0E, 0x12, 0x20, 0x12, 0x12, 0x20, 0x48, 0x2F, 0x27, 0x2F, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48 }; static __u8 qtable2[] = { 0x09, 0x06, 0x06, 0x09, 0x06, 0x06, 0x09, 0x09, 0x09, 0x09, 0x0C, 0x09, 0x09, 0x0C, 0x0F, 0x18, 0x0F, 0x0F, 0x0C, 0x0C, 0x0F, 0x1E, 0x15, 0x15, 0x12, 0x18, 0x24, 0x1E, 0x24, 0x24, 0x21, 0x1E, 0x21, 0x21, 0x27, 0x2A, 0x36, 0x30, 0x27, 0x2A, 0x33, 0x2A, 0x21, 0x21, 0x30, 0x42, 0x30, 0x33, 0x39, 0x3C, 0x3F, 0x3F, 0x3F, 0x24, 0x2D, 0x45, 0x48, 0x42, 0x3C, 0x48, 0x36, 0x3C, 0x3F, 0x3C, 0x09, 0x0C, 0x0C, 0x0F, 0x0C, 0x0F, 0x1B, 0x0F, 0x0F, 0x1B, 0x3C, 0x27, 0x21, 0x27, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C }; #endif static __u8 qtable3[] = { 0x07, 0x05, 0x05, 0x07, 0x05, 0x05, 0x07, 0x07, 0x07, 0x07, 0x0A, 0x07, 0x07, 0x0A, 0x0C, 0x14, 0x0C, 0x0C, 0x0A, 0x0A, 0x0C, 0x19, 0x11, 0x11, 0x0F, 0x14, 0x1E, 0x19, 0x1E, 0x1E, 0x1B, 0x19, 0x1B, 0x1B, 0x20, 0x23, 0x2D, 0x28, 0x20, 0x23, 0x2A, 0x23, 0x1B, 0x1B, 0x28, 0x37, 0x28, 0x2A, 0x2F, 0x32, 0x34, 0x34, 0x34, 0x1E, 0x25, 0x39, 0x3C, 0x37, 0x32, 0x3C, 0x2D, 0x32, 0x34, 0x32, 0x07, 0x0A, 0x0A, 0x0C, 0x0A, 0x0C, 0x16, 0x0C, 0x0C, 0x16, 0x32, 0x20, 0x1B, 0x20, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32 }; #if 0 static __u8 qtable4[] = { 0x06, 0x04, 0x04, 0x06, 0x04, 0x04, 0x06, 0x06, 0x06, 0x06, 0x08, 0x06, 0x06, 0x08, 0x0A, 0x11, 0x0A, 0x0A, 0x08, 0x08, 0x0A, 0x15, 0x0F, 0x0F, 0x0C, 0x11, 0x19, 0x15, 0x19, 0x19, 0x17, 0x15, 0x17, 0x17, 0x1B, 0x1D, 0x25, 0x21, 0x1B, 0x1D, 0x23, 0x1D, 0x17, 0x17, 0x21, 0x2E, 0x21, 0x23, 0x27, 0x29, 0x2C, 0x2C, 0x2C, 0x19, 0x1F, 0x30, 0x32, 0x2E, 0x29, 0x32, 0x25, 0x29, 0x2C, 0x29, 0x06, 0x08, 0x08, 0x0A, 0x08, 0x0A, 0x13, 0x0A, 0x0A, 0x13, 0x29, 0x1B, 0x17, 0x1B, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29 }; static __u8 qtable5[] = { 0x05, 0x03, 0x03, 0x05, 0x03, 0x03, 0x05, 0x05, 0x05, 0x05, 0x07, 0x05, 0x05, 0x07, 0x09, 0x0E, 0x09, 0x09, 0x07, 0x07, 0x09, 0x11, 0x0C, 0x0C, 0x0A, 0x0E, 0x15, 0x11, 0x15, 0x15, 0x13, 0x11, 0x13, 0x13, 0x16, 0x18, 0x1F, 0x1C, 0x16, 0x18, 0x1D, 0x18, 0x13, 0x13, 0x1C, 0x26, 0x1C, 0x1D, 0x21, 0x23, 0x24, 0x24, 0x24, 0x15, 0x1A, 0x28, 0x29, 0x26, 0x23, 0x29, 0x1F, 0x23, 0x24, 0x23, 0x05, 0x07, 0x07, 0x09, 0x07, 0x09, 0x10, 0x09, 0x09, 0x10, 0x23, 0x16, 0x13, 0x16, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23 }; static __u8 qtable6[] = { 0x04, 0x03, 0x03, 0x04, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x06, 0x04, 0x04, 0x06, 0x07, 0x0C, 0x07, 0x07, 0x06, 0x06, 0x07, 0x0E, 0x0A, 0x0A, 0x09, 0x0C, 0x11, 0x0E, 0x11, 0x11, 0x10, 0x0E, 0x10, 0x10, 0x13, 0x14, 0x1A, 0x17, 0x13, 0x14, 0x18, 0x14, 0x10, 0x10, 0x17, 0x20, 0x17, 0x18, 0x1B, 0x1D, 0x1E, 0x1E, 0x1E, 0x11, 0x16, 0x21, 0x23, 0x20, 0x1D, 0x23, 0x1A, 0x1D, 0x1E, 0x1D, 0x04, 0x06, 0x06, 0x07, 0x06, 0x07, 0x0D, 0x07, 0x07, 0x0D, 0x1D, 0x13, 0x10, 0x13, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D }; static __u8 qtable7[] = { 0x04, 0x02, 0x02, 0x04, 0x02, 0x02, 0x04, 0x04, 0x04, 0x04, 0x05, 0x04, 0x04, 0x05, 0x06, 0x0A, 0x06, 0x06, 0x05, 0x05, 0x06, 0x0C, 0x08, 0x08, 0x07, 0x0A, 0x0E, 0x0C, 0x0E, 0x0E, 0x0D, 0x0C, 0x0D, 0x0D, 0x10, 0x11, 0x16, 0x13, 0x10, 0x11, 0x14, 0x11, 0x0D, 0x0D, 0x13, 0x1A, 0x13, 0x14, 0x17, 0x18, 0x19, 0x19, 0x19, 0x0E, 0x12, 0x1C, 0x1D, 0x1A, 0x18, 0x1D, 0x16, 0x18, 0x19, 0x18, 0x04, 0x05, 0x05, 0x06, 0x05, 0x06, 0x0B, 0x06, 0x06, 0x0B, 0x18, 0x10, 0x0D, 0x10, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18 }; static __u8 qtable8[] = { 0x03, 0x02, 0x02, 0x03, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x03, 0x03, 0x04, 0x05, 0x08, 0x05, 0x05, 0x04, 0x04, 0x05, 0x0A, 0x07, 0x07, 0x06, 0x08, 0x0C, 0x0A, 0x0C, 0x0C, 0x0B, 0x0A, 0x0B, 0x0B, 0x0D, 0x0E, 0x12, 0x10, 0x0D, 0x0E, 0x11, 0x0E, 0x0B, 0x0B, 0x10, 0x16, 0x10, 0x11, 0x13, 0x14, 0x15, 0x15, 0x15, 0x0C, 0x0F, 0x17, 0x18, 0x16, 0x14, 0x18, 0x12, 0x14, 0x15, 0x14, 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x09, 0x05, 0x05, 0x09, 0x14, 0x0D, 0x0B, 0x0D, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14 }; #endif static int sn9c102p_i2cwrite(struct usb_spca50x *spca50x, __u8 * buffer, __u16 length) { struct usb_device *dev = spca50x->dev; __u8 mode[] = { 0x81, 0x11, 0, 0, 0, 0, 0, 0x10 }; /* is i2c ready */ __u8 i2cbase = spca50x->i2c_base; __u8 i2cCtrl = spca50x->i2c_ctrl_reg; mode[0] = i2cCtrl; mode[1] = i2cbase; if (length > 5 || !buffer) return -1; mode[0] = mode[0] | length << 4; memcpy(&mode[2], buffer, length); usb_wr_vend_int(dev, 0x08, 0x08, 0x0000, mode, 8); return 0; } static void sn9c102p_i2cwritebuf(struct usb_device *dev, __u8 * buffer) { usb_wr_vend_int(dev, 0x08, 0x08, 0x0000, buffer, 8); } static int sn9c102p_i2cread(struct usb_spca50x *spca50x, __u8 reg, __u8 * buffer, __u16 length) { struct usb_device *dev = spca50x->dev; __u8 i2cbase = spca50x->i2c_base; __u8 i2cCtrl = spca50x->i2c_ctrl_reg; __u8 mode[] = { 0x91, 0x11, 0, 0, 0, 0, 0, 0x10 }; __u8 result[] = { 0, 0, 0, 0, 0 }; mode[0] = i2cCtrl | 0x10; mode[1] = i2cbase; if (length > 5 || !buffer) return -1; mode[2] = reg; usb_wr_vend_int(dev, 0x08, 0x08, 0x0000, mode, 8); wait_ms(1); mode[2] = 0; mode[0] = i2cCtrl | length << 4 | 0x02; usb_wr_vend_int(dev, 0x08, 0x08, 0x0000, mode, 8); wait_ms(1); usb_rd_vend_int(dev, 0x00, 0x0a, 0x0000, result, 5); memcpy(buffer, result, length); return 0; } static int sn9cxxx_probesensor(struct usb_spca50x *spca50x) { struct usb_device *dev = spca50x->dev; int err = 0; __u8 reg02 = 0x66; /* reg val1 val2 val3 val4 */ __u8 datasend[] = { 0, 0, 0, 0, 0 }; __u8 datarecd[] = { 0, 0, 0, 0, 0 }; datasend[0] = 2; //sensor wakeup err = sn9c102p_i2cwrite(spca50x, datasend, 2); //should write 0xa1 0x11 0x02 0x00 0x00 0x00 0x00 the 0x10 is add by i2cw wait_ms(10); usb_wr_vend_int(dev, 0x08, 0x02, 0x0000, ®02, 1); //Gpio on wait_ms(10); err += sn9c102p_i2cread(spca50x, 0, datarecd, 5); //read sensor id if (err) goto errors; if (datarecd[0] == 0x02 && datarecd[1] == 0x09 && datarecd[2] == 0x01 && datarecd[3] == 0x00 && datarecd[4] == 0x00) { PDEBUG(1, "Find Sensor sn9c102P HV7131R"); PWC_SC(spca50x)->pwc_info.sensor = SENSOR_HV7131R; return SENSOR_HV7131R; } PDEBUG(0, "Find Sensor %d %d %d", datarecd[0], datarecd[1], datarecd[2]); PDEBUG(0, "Sensor sn9c102P Not found Contact mxhaard@free.fr "); return -ENODEV; errors:PDEBUG(0, "Sensor sn9c102P too many errors Contact mxhaard@free.fr "); return -ENODEV; } static void hv7131R_InitSensor(struct usb_spca50x *spca50x) { int i = 0; struct usb_device *dev = spca50x->dev; __u8 CtrlA320[] = { 0xA1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10 }; //Mclk //__u8 CtrlA640[] = { 0xA1, 0x11, 0x01, 0x18, 0x00, 0x00, 0x00, 0x10 };//Mclk/2 BOUMM __u8 CtrlA640[] = { 0xA1, 0x11, 0x01, 0x28, 0x00, 0x00, 0x00, 0x10 }; //Mckl/4 while (hv7131r_sensor_init[i][0]) { sn9c102p_i2cwritebuf(dev, hv7131r_sensor_init[i]); i++; } if (spca50x->mode) sn9c102p_i2cwritebuf(dev, CtrlA320); else sn9c102p_i2cwritebuf(dev, CtrlA640); } static void mi0360_InitSensor(struct usb_spca50x *spca50x) { int i = 0; struct usb_device *dev = spca50x->dev; while (mi0360_sensor_init[i][0]) { sn9c102p_i2cwritebuf(dev, hv7131r_sensor_init[i]); i++; } } static int sn9cxxx_init(struct usb_spca50x *spca50x) { struct usb_device *dev = spca50x->dev; int err = 0; __u8 *sn9c1xx = NULL; __u8 regF1 = 0x01; __u8 regGpio[] = { 0x29, 0x74 }; __u8 data = 0x00; /* setup a selector by customid */ switch (PWC_SC(spca50x)->pwc_info.sensor) { case SENSOR_HV7131R: sn9c1xx = sn_hv7131; break; case SENSOR_MI0360: sn9c1xx = sn_mi0360; break; } if (sn9c1xx == NULL) return -ENODEV; usb_wr_vend_int(dev, 0x08, 0xf1, 0x0000, ®F1, 1); usb_rd_vend_int(dev, 0x00, 0x00, 0x0000, ®F1, 1); usb_wr_vend_int(dev, 0x08, 0xf1, 0x0000, ®F1, 1); usb_rd_vend_int(dev, 0x00, 0x00, 0x0000, ®F1, 1); switch (spca50x->customid) { case SN9C102P: if (regF1 != 0x11) return -ENODEV; usb_wr_vend_int(dev, 0x08, 0x02, 0x0000, ®Gpio[1], 1); case SN9C105: if (regF1 != 0x11) return -ENODEV; usb_wr_vend_int(dev, 0x08, 0x02, 0x0000, regGpio, 2); break; case SN9C120: if (regF1 != 0x12) return -ENODEV; regGpio[1] = 0x70; usb_wr_vend_int(dev, 0x08, 0x02, 0x0000, regGpio, 2); break; } regF1 = 0x01; usb_wr_vend_int(dev, 0x08, 0xf1, 0x0000, ®F1, 1); regF1 = 0x00; usb_wr_vend_int(dev, 0x08, 0xf1, 0x0000, ®F1, 1); usb_wr_vend_int(dev, 0x08, 0x01, 0x0000, &sn9c1xx[1], 1); // configure gpio usb_wr_vend_int(dev, 0x08, 0x01, 0x0000, &sn9c1xx[1], 2); usb_wr_vend_int(dev, 0x08, 0x08, 0x0000, &sn9c1xx[8], 2); usb_wr_vend_int(dev, 0x08, 0x017, 0x0000, &sn9c1xx[0x17], 3); usb_wr_vend_int(dev, 0x08, 0x9a, 0x0000, reg9a, 6); data = 0x60; usb_wr_vend_int(dev, 0x08, 0xD4, 0x0000, &data, 1); usb_wr_vend_int(dev, 0x08, 0x03, 0x0000, &sn9c1xx[3], 0x0f); data = 0x43; usb_wr_vend_int(dev, 0x08, 0x01, 0x0000, &data, 1); data = 0x61; usb_wr_vend_int(dev, 0x08, 0x17, 0x0000, &data, 1); data = 0x42; usb_wr_vend_int(dev, 0x08, 0x01, 0x0000, &data, 1); switch (PWC_SC(spca50x)->pwc_info.sensor) { case SENSOR_HV7131R: // probe sensor if ((err = sn9cxxx_probesensor(spca50x)) < 0) return -ENODEV; break; } return 0; } static void sn9cxxx_stop(struct usb_spca50x *spca50x) { struct usb_device *dev = spca50x->dev; __u8 stophv7131[] = { 0xA1, 0x11, 0x02, 0x09, 0x00, 0x00, 0x00, 0x10 }; __u8 stopmi0360[] = { 0xB1, 0x5D, 0x07, 0x00, 0x00, 0x00, 0x00, 0x10 }; __u8 regF1 = 0x01; __u8 data = 0x0b; __u8 *sn9c1xx = NULL; switch (PWC_SC(spca50x)->pwc_info.sensor) { case SENSOR_HV7131R: sn9c1xx = sn_hv7131; data = 0x2b; sn9c102p_i2cwritebuf(dev, stophv7131); break; case SENSOR_MI0360: sn9c1xx = sn_mi0360; data = 0x29; sn9c102p_i2cwritebuf(dev, stopmi0360); break; } if (sn9c1xx == NULL) return; usb_wr_vend_int(dev, 0x08, 0x01, 0x0000, &sn9c1xx[1], 1); usb_wr_vend_int(dev, 0x08, 0x17, 0x0000, &sn9c1xx[0x17], 1); usb_wr_vend_int(dev, 0x08, 0x01, 0x0000, &sn9c1xx[1], 1); usb_wr_vend_int(dev, 0x08, 0x01, 0x0000, &data, 1); usb_wr_vend_int(dev, 0x08, 0xf1, 0x0000, ®F1, 1); } static void sn9cxxx_start(struct usb_spca50x *spca50x) { struct usb_device *dev = spca50x->dev; int i; __u8 DC29[] = { 0x6a, 0x50, 0x00, 0x00, 0x50, 0x3c }; __u8 C0[] = { 0x2d, 0x2d, 0x3a, 0x05, 0x04, 0x3f }; __u8 CA[] = { 0x28, 0xd8, 0x14, 0xec }; //HV7131 __u8 CE[]={0x28, 0xec, 0x1e, 0xec }; __u8 CE[] = { 0x32, 0xdd, 0x2d, 0xdd }; //MI0360 //__u8 gain[] = { 0xB1, 0x5D, 0x35, 0x00, 0x67, 0x00, 0x00, 0x10 }; //gain //__u8 doit[] = { 0xB1, 0x5D, 0x07, 0x00, 0x03, 0x00, 0x00, 0x10 }; //update sensor //__u8 sensorgo[] = { 0xB1, 0x5D, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10 }; //sensor on //__u8 expo[]={ 0xB1,0x5D,0x09,0x06,0x35,0x00,0x00,0x10 };// exposure 0x0635 -> 4 fp/s __u8 data = 0; __u8 regF1 = 0x00; int err = 0; __u8 *sn9c1xx = NULL; switch (PWC_SC(spca50x)->pwc_info.sensor) { case SENSOR_HV7131R: sn9c1xx = sn_hv7131; break; case SENSOR_MI0360: sn9c1xx = sn_mi0360; break; } if (sn9c1xx == NULL) return; usb_wr_vend_int(dev, 0x08, 0xf1, 0x0000, ®F1, 1); usb_wr_vend_int(dev, 0x08, 0x01, 0x0000, &sn9c1xx[1], 1); usb_wr_vend_int(dev, 0x08, 0x01, 0x0000, &sn9c1xx[1], 2); usb_wr_vend_int(dev, 0x08, 0x08, 0x0000, &sn9c1xx[8], 2); usb_wr_vend_int(dev, 0x08, 0x17, 0x0000, &sn9c1xx[0x17], 3); usb_wr_vend_int(dev, 0x08, 0x9a, 0x0000, reg9a, 6); data = 0x60; usb_wr_vend_int(dev, 0x08, 0xD4, 0x0000, &data, 1); usb_wr_vend_int(dev, 0x08, 0x03, 0x0000, &sn9c1xx[3], 0x0f); data = 0x43; usb_wr_vend_int(dev, 0x08, 0x01, 0x0000, &data, 1); data = 0x61; usb_wr_vend_int(dev, 0x08, 0x17, 0x0000, &data, 1); data = 0x42; usb_wr_vend_int(dev, 0x08, 0x01, 0x0000, &data, 1); switch (PWC_SC(spca50x)->pwc_info.sensor) { case SENSOR_HV7131R: if ((err = sn9cxxx_probesensor(spca50x)) < 0) return; break; } usb_wr_vend_int(dev, 0x08, 0x15, 0x0000, &sn9c1xx[0x15], 1); usb_wr_vend_int(dev, 0x08, 0x16, 0x0000, &sn9c1xx[0x16], 1); usb_wr_vend_int(dev, 0x08, 0x12, 0x0000, &sn9c1xx[0x12], 1); usb_wr_vend_int(dev, 0x08, 0x13, 0x0000, &sn9c1xx[0x13], 1); usb_wr_vend_int(dev, 0x08, 0x18, 0x0000, &sn9c1xx[0x18], 1); usb_wr_vend_int(dev, 0x08, 0xd2, 0x0000, &DC29[0], 1); usb_wr_vend_int(dev, 0x08, 0xd3, 0x0000, &DC29[1], 1); usb_wr_vend_int(dev, 0x08, 0xc6, 0x0000, &DC29[2], 1); usb_wr_vend_int(dev, 0x08, 0xc7, 0x0000, &DC29[3], 1); usb_wr_vend_int(dev, 0x08, 0xc8, 0x0000, &DC29[4], 1); usb_wr_vend_int(dev, 0x08, 0xc9, 0x0000, &DC29[5], 1); usb_wr_vend_int(dev, 0x08, 0x18, 0x0000, &sn9c1xx[0x18], 1); data = 0x60; usb_wr_vend_int(dev, 0x08, 0x17, 0x0000, &data, 1); usb_wr_vend_int(dev, 0x08, 0x05, 0x0000, &sn9c1xx[5], 1); usb_wr_vend_int(dev, 0x08, 0x07, 0x0000, &sn9c1xx[7], 1); usb_wr_vend_int(dev, 0x08, 0x06, 0x0000, &sn9c1xx[6], 1); usb_wr_vend_int(dev, 0x08, 0x14, 0x0000, &sn9c1xx[0x14], 1); usb_wr_vend_int(dev, 0x08, 0x20, 0x0000, regsn20, 0x11); for (i = 0; i < 8; i++) usb_wr_vend_int(dev, 0x08, 0x84, 0x0000, reg84, 0x15); data = 0x08; usb_wr_vend_int(dev, 0x08, 0x9a, 0x0000, &data, 1); data = 0x59; usb_wr_vend_int(dev, 0x08, 0x99, 0x0000, &data, 1); switch (PWC_SC(spca50x)->pwc_info.sensor) { case SENSOR_HV7131R: hv7131R_InitSensor(spca50x); break; case SENSOR_MI0360: mi0360_InitSensor(spca50x); break; } usb_wr_vend_int(dev, 0x08, 0xc0, 0x0000, C0, 6); usb_wr_vend_int(dev, 0x08, 0xca, 0x0000, CA, 4); usb_wr_vend_int(dev, 0x08, 0xce, 0x0000, CE, 4); //?? {0x1e,0xdd,0x2d,0xe7} // here change size mode 0 -> VGA; 1 -> CIF data = 0x40 | sn9c1xx[0x18] | (spca50x->mode << 4); usb_wr_vend_int(dev, 0x08, 0x18, 0x0000, &data, 1); usb_wr_vend_int(dev, 0x08, 0x100, 0x0000, qtable3, 0x40); usb_wr_vend_int(dev, 0x08, 0x140, 0x0000, qtable3 + 0x40, 0x40); data = sn9c1xx[0x18] | (spca50x->mode << 4); usb_wr_vend_int(dev, 0x08, 0x18, 0x0000, &data, 1); data = 0x02; //0x42 usb_wr_vend_int(dev, 0x08, 0x01, 0x0000, &data, 1); data = 0x61; //0x61 usb_wr_vend_int(dev, 0x08, 0x17, 0x0000, &data, 1); if (spca50x->mode) data = 0x06; //320 06clk 12Mhz else { if (PWC_SC(spca50x)->pwc_info.sensor == SENSOR_MI0360) { data = 0x65; //0x61 usb_wr_vend_int(dev, 0x08, 0x17, 0x0000, &data, 1); } data = 0x46; //640 clk 24Mz 46 } // enable video on usb_wr_vend_int(dev, 0x08, 0x01, 0x0000, &data, 1); err = sn9cxxx_setbrightness(spca50x); err = sn9cxxx_setcontrast(spca50x); /*FIXME Need to be in the brightness exposure setting */ /* if (spca50x->sensor == SENSOR_MI0360) { sn9c102p_i2cwritebuf(dev, gain); // sn9c102p_i2cwritebuf (dev,expo); sn9c102p_i2cwritebuf(dev, doit); sn9c102p_i2cwritebuf(dev, sensorgo); } */ //sn9c102p_i2cwritebuf (dev,PreAInit); //sn9c102p_i2cwritebuf (dev,RInit); } static unsigned int sn9cxxx_getexposure(struct usb_spca50x *spca50x) { __u8 expo[] = { 0, 0, 0, 0, 0 }; int err = 0; switch (PWC_SC(spca50x)->pwc_info.sensor) { case SENSOR_HV7131R: err += sn9c102p_i2cread(spca50x, 0x25, expo, 5); //read sensor exposure return (unsigned int) (expo[0] << 16 | expo[1] << 8 | expo[2]); break; case SENSOR_MI0360: err += sn9c102p_i2cread(spca50x, 0x09, expo, 5); //read sensor exposure return (unsigned int) (expo[0] << 8 | expo[1]); break; } return 0; } static unsigned int sn9cxxx_setexposure(struct usb_spca50x *spca50x, unsigned int expo) { __u8 Expodoit[] = { 0xc1, 0x11, 0x25, 0x07, 0x27, 0xc0, 0x00, 0x16 }; __u8 doit[] = { 0xB1, 0x5D, 0x07, 0x00, 0x03, 0x00, 0x00, 0x10 }; //update sensor __u8 sensorgo[] = { 0xB1, 0x5D, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10 }; //sensor on __u8 expoMi[] = { 0xB1, 0x5D, 0x09, 0x06, 0x35, 0x00, 0x00, 0x10 }; // exposure 0x0635 -> 4 fp/s unsigned int expotimes = expo; unsigned int expotimesret = 0; switch (PWC_SC(spca50x)->pwc_info.sensor) { case SENSOR_HV7131R: Expodoit[3] = (expotimes >> 16) & 0xff; Expodoit[4] = (expotimes >> 8) & 0xff; Expodoit[5] = (expotimes & 0xff); sn9c102p_i2cwritebuf(spca50x->dev, Expodoit); wait_ms(10); expotimesret = sn9cxxx_getexposure(spca50x); PDEBUG(3, "Exposure set %d ret %d ", expotimes, expotimesret); return expotimesret; break; case SENSOR_MI0360: if (expo > 0x0635) expo = 0x0635; //if (expo < 0x0100) // expo = 0x0100; if (expo < 0x003F) expo = 0x003F; expoMi[3] = (expo >> 8) & 0xFF; expoMi[4] = expo & 0xff; sn9c102p_i2cwritebuf(spca50x->dev, expoMi); sn9c102p_i2cwritebuf(spca50x->dev, doit); sn9c102p_i2cwritebuf(spca50x->dev, sensorgo); expotimesret = sn9cxxx_getexposure(spca50x); PDEBUG(3, "Exposure set %d ret %d ", expotimes, expotimesret); return expotimesret; break; } return 0; } static __u16 sn9cxxx_setbrightness(struct usb_spca50x *spca50x) { unsigned int expo, expotimesret; __u8 k2; switch (PWC_SC(spca50x)->pwc_info.sensor) { case SENSOR_HV7131R: expo = spca50x->brightness << 4; if (expo > 0x002dc6c0) expo = 0x002dc6c0; if (expo < 0x02a0) expo = 0x02a0; expotimesret = sn9cxxx_setexposure(spca50x, expo); break; case SENSOR_MI0360: expo = spca50x->brightness >> 4; expotimesret = sn9cxxx_setexposure(spca50x, expo); break; } k2 = spca50x->brightness >> 10; usb_wr_vend_int(spca50x->dev, 0x08, 0x96, 0x0000, &k2, 1); return 0; } static __u16 sn9cxxx_getbrightness(struct usb_spca50x *spca50x) { /* hardcoded registers seem not readable */ switch (PWC_SC(spca50x)->pwc_info.sensor) { case SENSOR_HV7131R: spca50x->brightness = 0x7fff; break; case SENSOR_MI0360: spca50x->brightness = 0x1fff; break; } return 0; } static __u16 sn9cxxx_setcontrast(struct usb_spca50x *spca50x) { __u8 k2; __u8 contrast[] = { 0x14, 0x00, 0x28, 0x00, 0x07, 0x00 }; k2 = spca50x->contrast >> 8; if (k2 > 0x7f) k2 = 0x7f; contrast[2] = k2; contrast[0] = (k2 + 1) >> 1; contrast[4] = (k2 + 1) / 5; usb_wr_vend_int(spca50x->dev, 0x08, 0x84, 0x0000, contrast, 6); return 0; } static __u16 sn9cxxx_getcontrast(struct usb_spca50x *spca50x) { /* hardcoded registers seem not readable */ spca50x->contrast = 0x3f << 8; //0x28 return 0; } #if 0 static void sn9cxxx_setAutobright(struct usb_spca50x *spca50x) { // GRR avg_lum from the header seem wrong set exposure on brightness instead // FIXME where is the avg luma value ?? unsigned int expotimes = 0; unsigned int expotimesret = 0; __u8 luma_mean = 110; __u8 luma_delta = 20; __u8 spring = 4; // 4 choice so the gain registers follow with a little retard int delta; spin_lock_irq(&spca50x->v4l_lock); delta = spca50x->avg_lum; spin_unlock_irq(&spca50x->v4l_lock); //PDEBUG(0,"Error setting exposure delta %d",delta); if ((delta < (luma_mean - luma_delta)) || (delta > (luma_mean + luma_delta))) { expotimes = sn9cxxx_getexposure(spca50x); expotimes += ((luma_mean - delta) >> spring); expotimesret = sn9cxxx_setexposure(spca50x, expotimes); if (expotimes != expotimesret) PDEBUG(0, "Error setting exposure !"); } } static void sn9cxxx_shutdown(struct usb_spca50x *spca50x) { } #endif #endif //SONIXJPGUSB pwcbsd/spca5xx-20060402/drivers/usb/sonix.h000755 000423 000000 00000052471 10553461760 020667 0ustar00luigiwheel000000 000000 #ifndef SONIXUSB_H #define SONIXUSB_H /**************************************************************************** # sonix sn9c102 library # # Copyright (C) 2003 2004 Michel Xhaard mxhaard@magic.fr # # Add Pas106 Stefano Mozzi (C) 2004 # # # # This program is free software; you can redistribute it and/or modify # # it under the terms of the GNU General Public License as published by # # the Free Software Foundation; either version 2 of the License, or # # (at your option) any later version. # # # # This program is distributed in the hope that it will be useful, # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # # ****************************************************************************/ #include "sn9cxxx.h" #define COMP2 0x8F #define COMP 0xC7 //0x87 //0x07 #define COMP1 0xC9 //0x89 //0x09 #define MCK_INIT 0x63 #define MCK_INIT1 0x20 #define SYS_CLK 0x04 /******************* Camera Interface ***********************/ static int sonix_init(struct usb_spca50x *spca50x); static void sonix_start(struct usb_spca50x *spca50x); static void sonix_stop(struct usb_spca50x *spca50x); static __u16 sonix_getbrightness(struct usb_spca50x *spca50x); static __u16 sonix_setbrightness(struct usb_spca50x *spca50x); static __u16 sonix_setcontrast(struct usb_spca50x *spca50x); static int sonix_config(struct usb_spca50x *spca50x); /******************************************************************/ static void set_sonixVGA(struct usb_spca50x *spca50x) { memset(spca50x->mode_cam, 0x00, TOTMODE * sizeof(struct mwebcam)); spca50x->mode_cam[VGA].width = 640; spca50x->mode_cam[VGA].height = 480; spca50x->mode_cam[VGA].t_palette = P_RAW | P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; if (spca50x->customid == SN9C102P || spca50x->customid == SN9C105 || spca50x->customid == SN9C120) spca50x->mode_cam[VGA].t_palette |= P_JPEG; spca50x->mode_cam[VGA].pipe = 1023; spca50x->mode_cam[VGA].method = 0; spca50x->mode_cam[VGA].mode = 0; spca50x->mode_cam[PAL].width = 384; spca50x->mode_cam[PAL].height = 288; spca50x->mode_cam[PAL].t_palette = P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[PAL].pipe = 1023; spca50x->mode_cam[PAL].method = 1; spca50x->mode_cam[PAL].mode = 0; spca50x->mode_cam[SIF].width = 352; spca50x->mode_cam[SIF].height = 288; spca50x->mode_cam[SIF].t_palette = P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[SIF].pipe = 1023; spca50x->mode_cam[SIF].method = 1; spca50x->mode_cam[SIF].mode = 0; spca50x->mode_cam[CIF].width = 320; spca50x->mode_cam[CIF].height = 240; spca50x->mode_cam[CIF].t_palette = P_RAW | P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; if (spca50x->customid == SN9C102P || spca50x->customid == SN9C105 || spca50x->customid == SN9C120) spca50x->mode_cam[CIF].t_palette |= P_JPEG; spca50x->mode_cam[CIF].pipe = 1023; spca50x->mode_cam[CIF].method = 0; spca50x->mode_cam[CIF].mode = 1; spca50x->mode_cam[QPAL].width = 192; spca50x->mode_cam[QPAL].height = 144; spca50x->mode_cam[QPAL].t_palette = P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[QPAL].pipe = 1023; spca50x->mode_cam[QPAL].method = 1; spca50x->mode_cam[QPAL].mode = 1; spca50x->mode_cam[QSIF].width = 176; spca50x->mode_cam[QSIF].height = 144; spca50x->mode_cam[QSIF].t_palette = P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[QSIF].pipe = 1023; spca50x->mode_cam[QSIF].method = 1; spca50x->mode_cam[QSIF].mode = 1; if (spca50x->customid == SN9C101 || spca50x->customid == SN9C102 || spca50x->customid == SN9C103) { spca50x->mode_cam[QCIF].width = 160; spca50x->mode_cam[QCIF].height = 120; spca50x->mode_cam[QCIF].t_palette = P_RAW | P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[QCIF].pipe = 1023; spca50x->mode_cam[QCIF].method = 0; spca50x->mode_cam[QCIF].mode = 2; } } /* XXX this is wrong, SIF and CIF are swapped. */ static void set_sonixSIF(struct usb_spca50x *spca50x) { memset(spca50x->mode_cam, 0x00, TOTMODE * sizeof(struct mwebcam)); spca50x->mode_cam[SIF].width = 352; spca50x->mode_cam[SIF].height = 288; spca50x->mode_cam[SIF].t_palette = P_RAW | P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[SIF].pipe = 1023; spca50x->mode_cam[SIF].method = 0; spca50x->mode_cam[SIF].mode = 0; spca50x->mode_cam[CIF].width = 320; spca50x->mode_cam[CIF].height = 240; spca50x->mode_cam[CIF].t_palette = P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[CIF].pipe = 1023; spca50x->mode_cam[CIF].method = 1; spca50x->mode_cam[CIF].mode = 0; spca50x->mode_cam[QPAL].width = 192; spca50x->mode_cam[QPAL].height = 144; spca50x->mode_cam[QPAL].t_palette = P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[QPAL].pipe = 1023; spca50x->mode_cam[QPAL].method = 1; spca50x->mode_cam[QPAL].mode = 0; spca50x->mode_cam[QSIF].width = 176; spca50x->mode_cam[QSIF].height = 144; spca50x->mode_cam[QSIF].t_palette = P_RAW | P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[QSIF].pipe = 1023; spca50x->mode_cam[QSIF].method = 0; spca50x->mode_cam[QSIF].mode = 1; spca50x->mode_cam[QCIF].width = 160; spca50x->mode_cam[QCIF].height = 120; spca50x->mode_cam[QCIF].t_palette = P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[QCIF].pipe = 1023; spca50x->mode_cam[QCIF].method = 1; spca50x->mode_cam[QCIF].mode = 1; } static __u8 initTas5130[] = { 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x69, 0x0c, 0x0a, 0x28, 0x1e, 0x60, COMP, MCK_INIT, 0x18, 0x10, 0x04, 0x03, 0x11, 0x0c }; static __u8 initPas106[] = { 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x01, 0x00, 0x16, 0x12, 0x28, COMP1, MCK_INIT1, 0x18, 0x10, 0x04, 0x03, 0x11, 0x0c }; static __u8 initOv7630[] = { 0x04, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x0a, //shift one pixel 0x02 is 0x01 at start 0x28, 0x1e, 0x68, COMP1, MCK_INIT1, 0x1d, 0x10, 0x02, 0x03, 0x0f, 0x0c }; static __u8 initHv7131[] = { 0x46, 0x77, 0x00, 0x04, 0x00, 0x00, 0x00, 0x80, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x00, // shift from 0x02 0x01 0x00 0x28, 0x1e, 0x60, 0x8a, 0x20, 0x1d, 0x10, 0x02, 0x03, 0x0f, 0x0c }; static __u8 initTas5110[] = { 0x44, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x46, 0x09, 0x0a, // shift from 0x45 0x09 0x0a 0x16, 0x12, 0x60, 0x86, 0x2b, 0x14, 0x0a, 0x02, 0x02, 0x09, 0x07 }; static __u8 initPas202[] = { 0x44, 0x44, 0x21, 0x30, 0x00, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x03, 0x0A, //6 0x28, 0x1e, 0x28, 0x89, 0x30, 0x00, 0x00, 0x02, 0x03, 0x0F, 0x0C }; //compression 0x86 mckinit1 0x2b static __u8 pas106_data[][2] = { {0x02, 0x04}, /* Pixel Clock Divider 6 */ {0x03, 0x13}, /* Frame Time MSB */ //{ 0x03, 0x12}, /* Frame Time MSB */ {0x04, 0x06}, /* Frame Time LSB */ //{ 0x04, 0x05}, /* Frame Time LSB */ {0x05, 0x65}, /* Shutter Time Line Offset */ //{ 0x05, 0x6d}, /* Shutter Time Line Offset */ //{ 0x06, 0xB1}, /* Shutter Time Pixel Offset */ {0x06, 0xcd}, /* Shutter Time Pixel Offset */ {0x07, 0xC1}, /* Black Level Subtract Sign */ //{ 0x07, 0x00}, /* Black Level Subtract Sign */ {0x08, 0x06}, /* Black Level Subtract Level */ {0x08, 0x06}, /* Black Level Subtract Level */ //{ 0x08, 0x01}, /* Black Level Subtract Level */ {0x09, 0x05}, /* Color Gain B Pixel 5 a */ {0x0A, 0x04}, /* Color Gain G1 Pixel 1 5 */ {0x0B, 0x04}, /* Color Gain G2 Pixel 1 0 5 */ {0x0C, 0x05}, /* Color Gain R Pixel 3 1 */ {0x0D, 0x00}, /* Color GainH Pixel */ {0x0E, 0x0E}, /* Global Gain */ {0x0F, 0x00}, /* Contrast */ {0x10, 0x06}, /* H&V synchro polarity */ {0x11, 0x06}, /* ?default */ {0x12, 0x06}, /* DAC scale */ {0x14, 0x02}, /* ?default */ {0x13, 0x01}, /* Validate Settings */ {0, 0} /* The end */ }; static __u8 ov7630_sensor_init[][8] = { {0xa0, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, {0xb0, 0x21, 0x01, 0x77, 0x3a, 0x00, 0x00, 0x10}, {0xd0, 0x21, 0x12, 0x78, 0x00, 0x80, 0x34, 0x10}, {0xa0, 0x21, 0x1b, 0x04, 0x00, 0x80, 0x34, 0x10}, {0xa0, 0x21, 0x20, 0x44, 0x00, 0x80, 0x34, 0x10}, {0xa0, 0x21, 0x23, 0xee, 0x00, 0x80, 0x34, 0x10}, {0xd0, 0x21, 0x26, 0xa0, 0x9a, 0xa0, 0x30, 0x10}, {0xb0, 0x21, 0x2a, 0x80, 0x00, 0xa0, 0x30, 0x10}, {0xb0, 0x21, 0x2f, 0x3d, 0x24, 0xa0, 0x30, 0x10}, {0xa0, 0x21, 0x32, 0x86, 0x24, 0xa0, 0x30, 0x10}, {0xb0, 0x21, 0x60, 0xa9, 0x42, 0xa0, 0x30, 0x10}, {0xa0, 0x21, 0x65, 0x00, 0x42, 0xa0, 0x30, 0x10}, {0xa0, 0x21, 0x69, 0x38, 0x42, 0xa0, 0x30, 0x10}, {0xc0, 0x21, 0x6f, 0x88, 0x0b, 0x00, 0x30, 0x10}, {0xc0, 0x21, 0x74, 0x21, 0x8e, 0x00, 0x30, 0x10}, {0xa0, 0x21, 0x7d, 0xf7, 0x8e, 0x00, 0x30, 0x10}, {0xd0, 0x21, 0x17, 0x1c, 0xbd, 0x06, 0xf6, 0x10}, // {0xa0, 0x21, 0x10, 0x36, 0xbd, 0x06, 0xf6, 0x16}, // exposure {0xa0, 0x21, 0x76, 0x03, 0xbd, 0x06, 0xf6, 0x16}, {0xa0, 0x21, 0x11, 0x01, 0xbd, 0x06, 0xf6, 0x16}, {0xa0, 0x21, 0x00, 0x10, 0xbd, 0x06, 0xf6, 0x15}, //gain //{ 0xb0, 0x21, 0x2a, 0xc0, 0x3c, 0x06, 0xf6, 0x1d},//a0 1c,a0 1f,c0 3c frame rate ?line interval from ov6630 {0xb0, 0x21, 0x2a, 0xa0, 0x1f, 0x06, 0xf6, 0x1d}, {0, 0, 0, 0, 0, 0, 0, 0} }; static __u8 tas5110_sensor_init[][8] = { {0x30, 0x11, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x10}, {0x30, 0x11, 0x02, 0x20, 0xa9, 0x00, 0x00, 0x10}, {0, 0, 0, 0, 0, 0, 0, 0} }; static __u8 hv7131_sensor_init[][8] = { {0xc0, 0x11, 0x31, 0x38, 0x2a, 0x2e, 0x00, 0x10}, {0xa0, 0x11, 0x01, 0x08, 0x2a, 0x2e, 0x00, 0x10}, {0xb0, 0x11, 0x20, 0x00, 0xd0, 0x2e, 0x00, 0x10}, {0xc0, 0x11, 0x25, 0x03, 0x0e, 0x28, 0x00, 0x16}, {0xa0, 0x11, 0x30, 0x10, 0x0e, 0x28, 0x00, 0x15}, {0, 0, 0, 0, 0, 0, 0, 0} }; static __u8 pas202_sensor_init[][8] = { {0xA0, 0x40, 0x02, 0x03, 0x00, 0x00, 0x00, 0x10}, {0xD0, 0x40, 0x04, 0x07, 0x34, 0x00, 0x09, 0x10}, {0xD0, 0x40, 0x08, 0x01, 0x00, 0x00, 0x01, 0x10}, {0xD0, 0x40, 0x0C, 0x00, 0x0C, 0x00, 0x32, 0x10}, {0xD0, 0x40, 0x10, 0x00, 0x01, 0x00, 0x63, 0x10}, {0xA0, 0x40, 0x15, 0x70, 0x01, 0x00, 0x63, 0x10}, {0xA0, 0x40, 0x18, 0x00, 0x01, 0x00, 0x63, 0x10}, {0xA0, 0x40, 0x11, 0x01, 0x01, 0x00, 0x63, 0x10}, {0xA0, 0x40, 0x03, 0x56, 0x01, 0x00, 0x63, 0x10}, {0xA0, 0x40, 0x11, 0x01, 0x01, 0x00, 0x63, 0x10}, {0xB0, 0x40, 0x04, 0x07, 0x2A, 0x00, 0x63, 0x10}, {0xB0, 0x40, 0x0E, 0x00, 0x3D, 0x00, 0x63, 0x10}, {0xA0, 0x40, 0x11, 0x01, 0x3D, 0x00, 0x63, 0x16}, {0xA0, 0x40, 0x10, 0x08, 0x3D, 0x00, 0x63, 0x15}, {0xA0, 0x40, 0x02, 0x04, 0x3D, 0x00, 0x63, 0x16}, {0xA0, 0x40, 0x11, 0x01, 0x3D, 0x00, 0x63, 0x16}, {0xB0, 0x40, 0x0E, 0x00, 0x31, 0x00, 0x63, 0x16}, {0xA0, 0x40, 0x11, 0x01, 0x31, 0x00, 0x63, 0x16}, {0xA0, 0x40, 0x10, 0x0E, 0x31, 0x00, 0x63, 0x15}, {0xA0, 0x40, 0x11, 0x01, 0x31, 0x00, 0x63, 0x16}, {0, 0, 0, 0, 0, 0, 0, 0} }; static int sonix_i2cwrite(struct usb_device *dev, __u8 * buffer, __u16 length) { int retry = 60; __u8 ByteReceive = 0x00; /* is i2c ready */ if (length > 8 || !buffer) return -1; usb_wr_vend_int(dev, 0x08 /* reg */, 0x08 /* value */, 0x0000 /* index */, buffer, length); while (retry--) { wait_ms(10); usb_rd_vend_int(dev, 0x00, 0x08, 0x0000, &ByteReceive, 1); if (ByteReceive == 4) return 0; } return -1; } static __u16 sonix_getbrightness(struct usb_spca50x *spca50x) { /*FIXME hardcoded as we need to read register of the tasc */ spca50x->brightness = 0x80 << 8; spca50x->contrast = 0x80 << 8; return (0x80 << 8); } static __u16 sonix_setbrightness(struct usb_spca50x *spca50x) { __u8 value; __u8 i2c[] = { 0x30, 0x11, 0x02, 0x20, 0x70, 0x00, 0x00, 0x10 }; __u8 i2c1[] = { 0xA1, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14 }; __u8 i2cOV[] = { 0xa0, 0x21, 0x06, 0x36, 0xbd, 0x06, 0xf6, 0x16 }; __u8 i2cp202[] = { 0xA0, 0x40, 0x10, 0x0E, 0x31, 0x00, 0x63, 0x15 }; __u8 i2cpdoit[] = { 0xA0, 0x40, 0x11, 0x01, 0x31, 0x00, 0x63, 0x16 }; //__u8 i2cpexpo1[] = { 0xB0,0x40,0x04,0x07,0x2A,0x00,0x63,0x16 }; __u8 i2cpexpo[] = { 0xB0, 0x40, 0x0e, 0x01, 0xab, 0x00, 0x63, 0x16 }; switch (PWC_SC(spca50x)->pwc_info.sensor) { case SENSOR_TAS5130C: case SENSOR_TAS5110: value = (0xFF - (spca50x->brightness >> 8)); i2c[4] = value & 0xFF; PDEBUG(4, "brightness %d :%d ", value, i2c[4]); if (sonix_i2cwrite(spca50x->dev, i2c, 8) < 0) PDEBUG(0, "i2c error brightness"); break; case SENSOR_PAS106: i2c1[3] = spca50x->brightness >> 11; i2c1[2] = 0x0e;; if (sonix_i2cwrite(spca50x->dev, i2c1, 8) < 0) PDEBUG(0, "i2c error brightness"); i2c1[3] = 0x01; i2c1[2] = 0x13;; if (sonix_i2cwrite(spca50x->dev, i2c1, 8) < 0) PDEBUG(0, "i2c error brightness"); break; case SENSOR_OV7630: // change reg 0x06 i2cOV[3] = (spca50x->brightness >> 8); if (sonix_i2cwrite(spca50x->dev, i2cOV, 8) < 0) PDEBUG(0, "i2c error brightness"); break; case SENSOR_PAS202: // change reg 0x10 i2cpexpo[4] = 0xff - (spca50x->brightness >> 8); //if(sonix_i2cwrite(spca50x->dev,i2cpexpo1,8) < 0) PDEBUG(0,"i2c error brightness"); //if(sonix_i2cwrite(spca50x->dev,i2cpdoit,8) < 0) PDEBUG(0,"i2c error brightness"); if (sonix_i2cwrite(spca50x->dev, i2cpexpo, 8) < 0) PDEBUG(0, "i2c error brightness"); if (sonix_i2cwrite(spca50x->dev, i2cpdoit, 8) < 0) PDEBUG(0, "i2c error brightness"); i2cp202[3] = (spca50x->brightness >> 11); if (sonix_i2cwrite(spca50x->dev, i2cp202, 8) < 0) PDEBUG(0, "i2c error brightness"); if (sonix_i2cwrite(spca50x->dev, i2cpdoit, 8) < 0) PDEBUG(0, "i2c error brightness"); break; } return 0; } static __u16 sonix_setcontrast(struct usb_spca50x *spca50x) { __u8 gain = 0; __u8 rgb_value = 0; gain = (spca50x->contrast >> 13) & 0x0F; /* red and blue gain */ rgb_value = gain << 4 | gain; usb_wr_vend_int(spca50x->dev, 0x08, 0x10, 0x0000, &rgb_value, 1); /* green gain */ rgb_value = gain; usb_wr_vend_int(spca50x->dev, 0x08, 0x11, 0x0000, &rgb_value, 1); return 0; } static int sonix_init(struct usb_spca50x *spca50x) { __u8 ByteReceive = 0x00; usb_rd_vend_int(spca50x->dev, 0x00, 0x00, 0x0000, &ByteReceive, 1); if (ByteReceive != 0x10) return -ENODEV; return 0; } static int tas5130_I2cinit(struct usb_spca50x *spca50x) { //__u8 i2c10[]= { 0x30,0x11,0x00,0x40,0x47,0x00,0x00,0x10 }; // shutter 0x47 short exposure? __u8 i2c10[] = { 0x30, 0x11, 0x00, 0x40, 0x01, 0x00, 0x00, 0x10 }; // shutter 0x01 long exposure __u8 i2c2[] = { 0x30, 0x11, 0x02, 0x20, 0x70, 0x00, 0x00, 0x10 }; if (sonix_i2cwrite(spca50x->dev, i2c10, 8) < 0) PDEBUG(0, "i2c error i2c10"); if (sonix_i2cwrite(spca50x->dev, i2c2, 8) < 0) PDEBUG(0, "i2c error i2c2"); return 0; } static int pas106_I2cinit(struct usb_spca50x *spca50x) { __u8 i2c1[] = { 0xA1, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14 }; int i = 0; while (pas106_data[i][0]) { memcpy(&i2c1[2], &pas106_data[i++][0], 2); //copy 2 bytes from the template if (sonix_i2cwrite(spca50x->dev, i2c1, 8) < 0) PDEBUG(0, "i2c error pas106"); } return 0; } static int ov7630_I2cinit(struct usb_spca50x *spca50x) { int i = 0; while (ov7630_sensor_init[i][0]) { if (sonix_i2cwrite(spca50x->dev, ov7630_sensor_init[i], 8) < 0) PDEBUG(0, "i2c error ov7630"); i++; } return 0; } static int tas5110_I2cinit(struct usb_spca50x *spca50x) { int i = 0; while (tas5110_sensor_init[i][0]) { if (sonix_i2cwrite(spca50x->dev, tas5110_sensor_init[i], 8) < 0) PDEBUG(0, "i2c error tas5110"); i++; } return 0; } static int hv7131_I2cinit(struct usb_spca50x *spca50x) { int i = 0; while (hv7131_sensor_init[i][0]) { if (sonix_i2cwrite(spca50x->dev, hv7131_sensor_init[i], 8) < 0) PDEBUG(0, "i2c error hv7131"); i++; } return 0; } static int pas202_I2cinit(struct usb_spca50x *spca50x) { int i = 0; while (pas202_sensor_init[i][0]) { if (sonix_i2cwrite(spca50x->dev, pas202_sensor_init[i], 8) < 0) PDEBUG(0, "i2c error pas202"); i++; } return 0; } static void sonix_start(struct usb_spca50x *spca50x) { __u8 compress = 0; __u8 MCK_SIZE = 0x33; __u8 frmult = 0x28; __u8 *sn9c10x = NULL; __u8 CompressCtrl[] = { 0, 0 }; int err = 0; switch (PWC_SC(spca50x)->pwc_info.sensor) { case SENSOR_OV7630: sn9c10x = initOv7630; compress = spca50x->mode << 4 | COMP2; CompressCtrl[0] = compress; frmult = 0x68; CompressCtrl[1] = 0x20; MCK_SIZE = 0x20; break; case SENSOR_TAS5130C: sn9c10x = initTas5130; compress = spca50x->mode << 4 | COMP; CompressCtrl[0] = compress; frmult = 0x60; switch (spca50x->mode) { case 0: /* 640x480 3fp/s */ CompressCtrl[1] = 0x43; //0xA3 3fp/s ;// 0xF3; MCK_SIZE = 0x43; // 0xA3;//0xF3; break; case 1: /* 320x240 0x33 10fp/s */ CompressCtrl[1] = 0x23; MCK_SIZE = 0x23; break; case 2: /* 160x120 15fp/s */ CompressCtrl[1] = 0x23; MCK_SIZE = 0x23; break; default: break; } break; case SENSOR_PAS106: sn9c10x = initPas106;; compress = spca50x->mode << 4 | COMP1; CompressCtrl[0] = compress; frmult = 0x24; //0x28 CompressCtrl[1] = 0x20; //0xF3; MCK_SIZE = 0x20; //0xF3; break; case SENSOR_TAS5110: sn9c10x = initTas5110; compress = spca50x->mode << 4 | 0x86; CompressCtrl[0] = compress; frmult = 0x60; CompressCtrl[1] = 0x2b; //0xF3; MCK_SIZE = 0x2b; //0xF3; break; case SENSOR_HV7131R: sn9c10x = initHv7131; compress = spca50x->mode << 4 | 0x8a; CompressCtrl[0] = compress; frmult = 0x60; CompressCtrl[1] = 0x20; MCK_SIZE = 0x20; break; case SENSOR_PAS202: sn9c10x = initPas202; compress = spca50x->mode << 4 | 0x89; CompressCtrl[0] = compress; frmult = 0x20; //reg17 // 7 fp/s VGA if (spca50x->mode) frmult = 0x24; // 0x28->11fp/s SIF 0x24 CompressCtrl[1] = 0x20; //reg19 30 MCK_SIZE = 0x20; break; } /* reg 0x01 bit 2 video transfert on */ usb_wr_vend_int(spca50x->dev, 0x08, 0x01, 0x0000, &sn9c10x[0], 1); /* reg 0x17 SensorClk enable inv Clk 0x60 */ usb_wr_vend_int(spca50x->dev, 0x08, 0x17, 0x0000, &sn9c10x[0x17 - 1], 1); /* Set the whole registers from the template */ usb_wr_vend_int(spca50x->dev, 0x08, 0x01, 0x0000, sn9c10x, 0x1f); switch (PWC_SC(spca50x)->pwc_info.sensor) { case SENSOR_TAS5130C: err = tas5130_I2cinit(spca50x); break; case SENSOR_PAS106: err = pas106_I2cinit(spca50x); break; case SENSOR_OV7630: err = ov7630_I2cinit(spca50x); break; case SENSOR_HV7131R: err = hv7131_I2cinit(spca50x); break; case SENSOR_TAS5110: err = tas5110_I2cinit(spca50x); break; case SENSOR_PAS202: err = pas202_I2cinit(spca50x); break; default: err = -EINVAL; break; } /* H_size V_size 0x28,0x1e maybe 640x480 */ usb_wr_vend_int(spca50x->dev, 0x08, 0x15, 0x0000, &sn9c10x[0x15 - 1], 0x02); /* compression register */ usb_wr_vend_int(spca50x->dev, 0x08, 0x18, 0x0000, &compress, 1); // H_start usb_wr_vend_int(spca50x->dev, 0x08, 0x12, 0x0000, &sn9c10x[0x12 - 1], 1); // V_START usb_wr_vend_int(spca50x->dev, 0x08, 0x13, 0x0000, &sn9c10x[0x13 - 1], 1); /* re set 0x17 SensorClk enable inv Clk 0x60 */ usb_wr_vend_int(spca50x->dev, 0x08, 0x17, 0x0000, &frmult, 1); /*MCKSIZE ->3 */ usb_wr_vend_int(spca50x->dev, 0x08, 0x19, 0x0000, &MCK_SIZE, 1); /* AE_STRX AE_STRY AE_ENDX AE_ENDY */ usb_wr_vend_int(spca50x->dev, 0x08, 0x1c, 0x0000, &sn9c10x[0x1c - 1], 4); /* Enable video transfert */ usb_wr_vend_int(spca50x->dev, 0x08, 0x01, 0x0000, &sn9c10x[0], 1); /* Compression */ usb_wr_vend_int(spca50x->dev, 0x08, 0x18, 0x0000, CompressCtrl, 2); sonix_setcontrast(spca50x); sonix_setbrightness(spca50x); } static int sonix_config(struct usb_spca50x *spca50x) { switch (PWC_SC(spca50x)->pwc_info.sensor) { case SENSOR_OV7630: case SENSOR_TAS5130C: case SENSOR_HV7131R: case SENSOR_MI0360: case SENSOR_PAS202: set_sonixVGA(spca50x); break; case SENSOR_PAS106: case SENSOR_TAS5110: set_sonixSIF(spca50x); break; default: return -EINVAL; break; } spca50x->qindex = 3; //set the quantization table return 0; } static void sonix_stop(struct usb_spca50x *spca50x) { __u8 ByteSend = 0; ByteSend = 0x09; // 0X00 usb_wr_vend_int(spca50x->dev, 0x08, 0x01, 0x0000, &ByteSend, 1); } #endif /* SONIXUSB_H */ pwcbsd/spca5xx-20060402/drivers/usb/sp5xxfw2.dat000744 000423 000000 00000015371 10546676142 021556 0ustar00luigiwheel000000 000000 /* Initialisation data for the Creative PC-CAM 600 */ static __u16 spca504_pccam600_init_data[][3] = { // {0xa0, 0x0000, 0x0503}, /* capture mode */ {0x00, 0x0000, 0x2000}, {0x00, 0x0013, 0x2301}, {0x00, 0x0003, 0x2000}, {0x00, 0x0001, 0x21ac}, {0x00, 0x0001, 0x21a6}, {0x00, 0x0000, 0x21a7}, /* brightness */ {0x00, 0x0020, 0x21a8}, /* contrast */ {0x00, 0x0001, 0x21ac}, /* sat/hue */ {0x00, 0x0000, 0x21ad}, /* hue */ {0x00, 0x001a, 0x21ae}, /* saturation */ {0x00, 0x0002, 0x21a3}, /* gamma */ #if 0 {0xb0, 0x0000, 0x0000}, /* reset auto exposure */ {0x0c, 0x0000, 0x0000}, /* reset auto whiteness */ {0x0c, 0x0004, 0x0000}, /* enable auto whiteness */ {0x30, 0x020f, 0x0001}, /* exposure compensation */ {0x30, 0x01f7, 0x0002}, /* whiteness balance */ #endif {0x30, 0x0154, 0x0008}, {0x30, 0x0004, 0x0006}, {0x30, 0x0258, 0x0009}, {0x30, 0x0004, 0x0000}, {0x30, 0x0093, 0x0004}, {0x30, 0x0066, 0x0005}, {0x00, 0x0000, 0x2000}, {0x00, 0x0013, 0x2301}, {0x00, 0x0003, 0x2000}, {0x00, 0x0013, 0x2301}, {0x00, 0x0003, 0x2000}, {0, 0, 0} }; /* Creative PC-CAM 600 specific open data, sent before using the * generic initialisation data from spca504_open_data. */ static __u16 spca504_pccam600_open_data[][3] = { {0x00, 0x0001, 0x2501}, {0x20, 0x0500, 0x0001}, /* snapshot mode */ {0x00, 0x0003, 0x2880}, {0x00, 0x0001, 0x2881}, {0, 0, 0} }; /* Initialisation data for the logitech clicksmart 420 */ static __u16 spca504A_clicksmart420_init_data[][3] = { // {0xa0, 0x0000, 0x0503}, /* capture mode */ {0x00, 0x0000, 0x2000}, {0x00, 0x0013, 0x2301}, {0x00, 0x0003, 0x2000}, {0x00, 0x0001, 0x21ac}, {0x00, 0x0001, 0x21a6}, {0x00, 0x0000, 0x21a7}, /* brightness */ {0x00, 0x0020, 0x21a8}, /* contrast */ {0x00, 0x0001, 0x21ac}, /* sat/hue */ {0x00, 0x0000, 0x21ad}, /* hue */ {0x00, 0x001a, 0x21ae}, /* saturation */ {0x00, 0x0002, 0x21a3}, /* gamma */ #if 1 {0x30, 0x0004, 0x000a}, {0xb0, 0x0001, 0x0000}, #endif #if 0 {0xb0, 0x0000, 0x0000}, /* reset auto exposure */ {0x0c, 0x0000, 0x0000}, /* reset auto whiteness */ {0x0c, 0x0004, 0x0000}, /* enable auto whiteness */ {0x30, 0x020f, 0x0001}, /* exposure compensation */ {0x30, 0x01f7, 0x0002}, /* whiteness balance */ #endif #if 1 {0x0a1, 0x0080, 0x0001}, {0x30, 0x0049, 0x0000}, {0x30, 0x0060, 0x0005}, {0x0c, 0x0004, 0x0000}, {0x00, 0x0000, 0x0000}, {0x00, 0x0000, 0x2000}, {0x00, 0x0013, 0x2301}, {0x00, 0x0003, 0x2000}, {0x00, 0x0000, 0x2000}, #endif #if 0 {0x30, 0x0154, 0x0008}, {0x30, 0x0004, 0x0006}, {0x30, 0x0258, 0x0009}, {0x30, 0x0004, 0x0000}, {0x30, 0x0093, 0x0004}, {0x30, 0x0066, 0x0005}, {0x00, 0x0000, 0x2000}, {0x00, 0x0013, 0x2301}, {0x00, 0x0003, 0x2000}, {0x00, 0x0013, 0x2301}, {0x00, 0x0003, 0x2000}, #endif {0, 0, 0} }; /* clicksmart 420 open data ? */ static __u16 spca504A_clicksmart420_open_data[][3] = { {0x00, 0x0001, 0x2501}, {0x20, 0x0502, 0x0000}, {0x06, 0x0000, 0x0000}, {0x00, 0x0004, 0x2880}, {0x00, 0x0001, 0x2881}, /* look like setting a qTable */ {0x00, 0x0006, 0x2800}, {0x00, 0x0004, 0x2801}, {0x00, 0x0004, 0x2802}, {0x00, 0x0006, 0x2803}, {0x00, 0x000a, 0x2804}, {0x00, 0x0010, 0x2805}, {0x00, 0x0014, 0x2806}, {0x00, 0x0018, 0x2807}, {0x00, 0x0005, 0x2808}, {0x00, 0x0005, 0x2809}, {0x00, 0x0006, 0x280a}, {0x00, 0x0008, 0x280b}, {0x00, 0x000a, 0x280c}, {0x00, 0x0017, 0x280d}, {0x00, 0x0018, 0x280e}, {0x00, 0x0016, 0x280f}, {0x00, 0x0006, 0x2810}, {0x00, 0x0005, 0x2811}, {0x00, 0x0006, 0x2812}, {0x00, 0x000a, 0x2813}, {0x00, 0x0010, 0x2814}, {0x00, 0x0017, 0x2815}, {0x00, 0x001c, 0x2816}, {0x00, 0x0016, 0x2817}, {0x00, 0x0006, 0x2818}, {0x00, 0x0007, 0x2819}, {0x00, 0x0009, 0x281a}, {0x00, 0x000c, 0x281b}, {0x00, 0x0014, 0x281c}, {0x00, 0x0023, 0x281d}, {0x00, 0x0020, 0x281e}, {0x00, 0x0019, 0x281f}, {0x00, 0x0007, 0x2820}, {0x00, 0x0009, 0x2821}, {0x00, 0x000f, 0x2822}, {0x00, 0x0016, 0x2823}, {0x00, 0x001b, 0x2824}, {0x00, 0x002c, 0x2825}, {0x00, 0x0029, 0x2826}, {0x00, 0x001f, 0x2827}, {0x00, 0x000a, 0x2828}, {0x00, 0x000e, 0x2829}, {0x00, 0x0016, 0x282a}, {0x00, 0x001a, 0x282b}, {0x00, 0x0020, 0x282c}, {0x00, 0x002a, 0x282d}, {0x00, 0x002d, 0x282e}, {0x00, 0x0025, 0x282f}, {0x00, 0x0014, 0x2830}, {0x00, 0x001a, 0x2831}, {0x00, 0x001f, 0x2832}, {0x00, 0x0023, 0x2833}, {0x00, 0x0029, 0x2834}, {0x00, 0x0030, 0x2835}, {0x00, 0x0030, 0x2836}, {0x00, 0x0028, 0x2837}, {0x00, 0x001d, 0x2838}, {0x00, 0x0025, 0x2839}, {0x00, 0x0026, 0x283a}, {0x00, 0x0027, 0x283b}, {0x00, 0x002d, 0x283c}, {0x00, 0x0028, 0x283d}, {0x00, 0x0029, 0x283e}, {0x00, 0x0028, 0x283f}, {0x00, 0x0007, 0x2840}, {0x00, 0x0007, 0x2841}, {0x00, 0x000a, 0x2842}, {0x00, 0x0013, 0x2843}, {0x00, 0x0028, 0x2844}, {0x00, 0x0028, 0x2845}, {0x00, 0x0028, 0x2846}, {0x00, 0x0028, 0x2847}, {0x00, 0x0007, 0x2848}, {0x00, 0x0008, 0x2849}, {0x00, 0x000a, 0x284a}, {0x00, 0x001a, 0x284b}, {0x00, 0x0028, 0x284c}, {0x00, 0x0028, 0x284d}, {0x00, 0x0028, 0x284e}, {0x00, 0x0028, 0x284f}, {0x00, 0x000a, 0x2850}, {0x00, 0x000a, 0x2851}, {0x00, 0x0016, 0x2852}, {0x00, 0x0028, 0x2853}, {0x00, 0x0028, 0x2854}, {0x00, 0x0028, 0x2855}, {0x00, 0x0028, 0x2856}, {0x00, 0x0028, 0x2857}, {0x00, 0x0013, 0x2858}, {0x00, 0x001a, 0x2859}, {0x00, 0x0028, 0x285a}, {0x00, 0x0028, 0x285b}, {0x00, 0x0028, 0x285c}, {0x00, 0x0028, 0x285d}, {0x00, 0x0028, 0x285e}, {0x00, 0x0028, 0x285f}, {0x00, 0x0028, 0x2860}, {0x00, 0x0028, 0x2861}, {0x00, 0x0028, 0x2862}, {0x00, 0x0028, 0x2863}, {0x00, 0x0028, 0x2864}, {0x00, 0x0028, 0x2865}, {0x00, 0x0028, 0x2866}, {0x00, 0x0028, 0x2867}, {0x00, 0x0028, 0x2868}, {0x00, 0x0028, 0x2869}, {0x00, 0x0028, 0x286a}, {0x00, 0x0028, 0x286b}, {0x00, 0x0028, 0x286c}, {0x00, 0x0028, 0x286d}, {0x00, 0x0028, 0x286e}, {0x00, 0x0028, 0x286f}, {0x00, 0x0028, 0x2870}, {0x00, 0x0028, 0x2871}, {0x00, 0x0028, 0x2872}, {0x00, 0x0028, 0x2873}, {0x00, 0x0028, 0x2874}, {0x00, 0x0028, 0x2875}, {0x00, 0x0028, 0x2876}, {0x00, 0x0028, 0x2877}, {0x00, 0x0028, 0x2878}, {0x00, 0x0028, 0x2879}, {0x00, 0x0028, 0x287a}, {0x00, 0x0028, 0x287b}, {0x00, 0x0028, 0x287c}, {0x00, 0x0028, 0x287d}, {0x00, 0x0028, 0x287e}, {0x00, 0x0028, 0x287f}, {0xa0, 0x0000, 0x0503}, {0, 0, 0} }; pwcbsd/spca5xx-20060402/drivers/usb/sp5xxfw2.h000755 000423 000000 00000060546 10553461760 021237 0ustar00luigiwheel000000 000000 #ifndef SP5XXFW2_H #define SP5XXFW2_H /**************************************************************************** # Sunplus spca504(abc) spca533 spca536 library # # Copyright (C) 2005 Michel Xhaard mxhaard@magic.fr # # # # This program is free software; you can redistribute it and/or modify # # it under the terms of the GNU General Public License as published by # # the Free Software Foundation; either version 2 of the License, or # # (at your option) any later version. # # # # This program is distributed in the hope that it will be useful, # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # # ****************************************************************************/ #define SPCA504_PCCAM600_OFFSET_SNAPSHOT 3 #define SPCA504_PCCAM600_OFFSET_COMPRESS 4 #define SPCA504_PCCAM600_OFFSET_MODE 5 #define SPCA504_PCCAM600_OFFSET_DATA 14 /* Frame packet header offsets for the spca533 */ #define SPCA533_OFFSET_DATA 16 #define SPCA533_OFFSET_FRAMSEQ 15 /* Frame packet header offsets for the spca536 */ #define SPCA536_OFFSET_DATA 4 #define SPCA536_OFFSET_FRAMSEQ 1 #include "sp5xxfw2.dat" static int sp5xxfw2_init(struct usb_spca50x *spca50x); static void sp5xxfw2_start(struct usb_spca50x *spca50x); static void sp5xxfw2_stop(struct usb_spca50x *spca50x); static __u16 sp5xxfw2_setbrightness(struct usb_spca50x *spca50x); static __u16 sp5xxfw2_getbrightness(struct usb_spca50x *spca50x); static __u16 sp5xxfw2_setcontrast(struct usb_spca50x *spca50x); static __u16 sp5xxfw2_getcontrast(struct usb_spca50x *spca50x); static __u16 sp5xxfw2_setcolors(struct usb_spca50x *spca50x); static __u16 sp5xxfw2_getcolors(struct usb_spca50x *spca50x); //static __u16 sp5xxfw2_setexposure(struct usb_spca50x *spca50x); //static __u16 sp5xxfw2_getexposure(struct usb_spca50x *spca50x); //static void spca5xxx_setAutobright (struct usb_spca50x *spca50x); static int sp5xxfw2_config(struct usb_spca50x *spca50x); //static void sp5xxfw2_shutdown(struct usb_spca50x *spca50x); /************************** Private *************************/ static void spca504B_SetSizeType(struct usb_spca50x *spca50x); static void spca504_acknowledged_command(struct usb_spca50x *spca50x, __u16 reg, __u16 idx, __u16 val); static void spca504A_acknowledged_command(struct usb_spca50x *spca50x, __u16 reg, __u16 idx, __u16 val, __u8 stat, __u8 count); static void spca504_wait_status(struct usb_spca50x *spca50x); static void spca50x_GetFirmware(struct usb_spca50x *spca50x); static int spca504B_PollingDataReady(struct usb_device *dev); static void spca504B_WaitCmdStatus(struct usb_spca50x *spca50x); static void spca504B_setQtable(struct usb_spca50x *spca50x); static void sp5xx_initContBrigHueRegisters(struct usb_spca50x *spca50x); /************************************************************/ static int sp5xxfw2_init(struct usb_spca50x *spca50x) { int rc; __u8 Data = 0; __u8 i; __u8 info[6]; int err_code; switch (PWC_SC(spca50x)->pwc_info.bridge) { case BRIDGE_SPCA504B:{ usb_wr_vend_dev(spca50x->dev, 0x1d, 0, 0, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0, 1, 0x2306, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0, 0, 0x0d04, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0, 0, 0x2000, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0, 0x13, 0x2301, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0, 0, 0x2306, NULL, 0); } // becare no break here init follow case BRIDGE_SPCA533: rc = spca504B_PollingDataReady(spca50x->dev); spca50x_GetFirmware(spca50x); break; case BRIDGE_SPCA536: spca50x_GetFirmware(spca50x); usb_rd_vend_dev(spca50x->dev, 0x00, 0, 0x5002, &Data, 1); Data = 0; usb_wr_vend_dev(spca50x->dev, 0x24, 0, 0, &Data, 1); usb_rd_vend_dev(spca50x->dev, 0x24, 0, 0, &Data, 1); rc = spca504B_PollingDataReady(spca50x->dev); usb_wr_vend_dev(spca50x->dev, 0x34, 0, 0, NULL, 0); spca504B_WaitCmdStatus(spca50x); break; case BRIDGE_SPCA504C: //pccam600 PDEBUG(2, "Opening SPCA504 (PC-CAM 600)"); spca50x_reg_write(spca50x->dev, 0xe0, 0x0000, 0x0000); spca50x_reg_write(spca50x->dev, 0xe0, 0x0000, 0x0001); // reset spca504_wait_status(spca50x); if (spca50x->desc == LogitechClickSmart420) { /* clicksmart 420 */ spca50x_write_vector(spca50x, spca504A_clicksmart420_open_data); } else { spca50x_write_vector(spca50x, spca504_pccam600_open_data); } err_code = spca50x_setup_qtable(spca50x, 0x00, 0x2800, 0x2840, qtable_creative_pccam); if (err_code < 0) { PDEBUG(2, "spca50x_setup_qtable failed"); return err_code; } break; case BRIDGE_SPCA504: PDEBUG(2, "Opening SPCA504"); if (spca50x->desc == AiptekMiniPenCam13) { /***************************************************************/ for (i = 0; i < 6; i++) { info[i] = spca50x_reg_read_with_value(spca50x->dev, 0x20, i, 0x0000, 1); } PDEBUG(0, "Read info: %d %d %d %d %d %d . Should be 1,0,2,2,0,0\n", info[0], info[1], info[2], info[3], info[4], info[5]); /* spca504a aiptek */ // Set AE AWB Banding Type 3-> 50Hz 2-> 60Hz spca504A_acknowledged_command(spca50x, 0x24, 8, 3, 0x9e, 1); // Twice sequencial need status 0xff->0x9e->0x9d spca504A_acknowledged_command(spca50x, 0x24, 8, 3, 0x9e, 0); spca504A_acknowledged_command(spca50x, 0x24, 0, 0, 0x9d, 1); /**************************************************************/ /* spca504a aiptek */ spca504A_acknowledged_command(spca50x, 0x08, 6, 0, 0x86, 1); // spca50x_reg_write (spca50x->dev, 0, 0x2000, 0); // spca50x_reg_write (spca50x->dev, 0, 0x2883, 1); // spca504A_acknowledged_command (spca50x, 0x08, 6, 0, 0x86, 1); //spca504A_acknowledged_command (spca50x, 0x24, 0, 0, 0x9D, 1); spca50x_reg_write(spca50x->dev, 0x0, 0x270c, 0x5); // L92 sno1t.txt spca50x_reg_write(spca50x->dev, 0x0, 0x2310, 0x5); spca504A_acknowledged_command(spca50x, 1, 0x0f, 0, 0xFF, 0); } /* setup qtable */ spca50x_reg_write(spca50x->dev, 0, 0x2000, 0); spca50x_reg_write(spca50x->dev, 0, 0x2883, 1); err_code = spca50x_setup_qtable(spca50x, 0x00, 0x2800, 0x2840, qtable_spca504_default); if (err_code < 0) { PDEBUG(2, "spca50x_setup_qtable failed"); return err_code; } break; } return 0; } static void sp5xxfw2_start(struct usb_spca50x *spca50x) { int rc; int enable; __u8 i; __u8 info[6]; if (PWC_SC(spca50x)->pwc_info.bridge == BRIDGE_SPCA504B) spca504B_setQtable(spca50x); spca504B_SetSizeType(spca50x); switch (PWC_SC(spca50x)->pwc_info.bridge) { case BRIDGE_SPCA504B: case BRIDGE_SPCA533: case BRIDGE_SPCA536: if (spca50x->desc == MegapixV4 || spca50x->desc == LogitechClickSmart820) { usb_wr_vend_dev(spca50x->dev, 0xF0, 0, 0, NULL, 0); spca504B_WaitCmdStatus(spca50x); usb_rd_vend_dev(spca50x->dev, 0xF0, 0, 4, NULL, 0); spca504B_WaitCmdStatus(spca50x); } else { usb_wr_vend_dev(spca50x->dev, 0x31, 0, 4, NULL, 0); spca504B_WaitCmdStatus(spca50x); rc = spca504B_PollingDataReady(spca50x->dev); } break; case BRIDGE_SPCA504: if (spca50x->desc == AiptekMiniPenCam13) { for (i = 0; i < 6; i++) { info[i] = spca50x_reg_read_with_value(spca50x->dev, 0x20, i, 0x0000, 1); } PDEBUG(0, "Read info: %d %d %d %d %d %d . Should be 1,0,2,2,0,0\n", info[0], info[1], info[2], info[3], info[4], info[5]); /* spca504a aiptek */ // Set AE AWB Banding Type 3-> 50Hz 2-> 60Hz spca504A_acknowledged_command(spca50x, 0x24, 8, 3, 0x9e, 1); // Twice sequencial need status 0xff->0x9e->0x9d spca504A_acknowledged_command(spca50x, 0x24, 8, 3, 0x9e, 0); spca504A_acknowledged_command(spca50x, 0x24, 0, 0, 0x9d, 1); } else { spca504_acknowledged_command(spca50x, 0x24, 8, 3); for (i = 0; i < 6; i++) { info[i] = spca50x_reg_read_with_value(spca50x->dev, 0x20, i, 0x0000, 1); } PDEBUG(0, "Read info: %d %d %d %d %d %d . Should be 1,0,2,2,0,0\n", info[0], info[1], info[2], info[3], info[4], info[5]); spca504_acknowledged_command(spca50x, 0x24, 8, 3); spca504_acknowledged_command(spca50x, 0x24, 0, 0); } spca504B_SetSizeType(spca50x); spca50x_reg_write(spca50x->dev, 0x0, 0x270c, 0x5); // L92 sno1t.txt spca50x_reg_write(spca50x->dev, 0x0, 0x2310, 0x5); break; case BRIDGE_SPCA504C: if (spca50x->desc == LogitechClickSmart420) { spca50x_write_vector(spca50x, spca504A_clicksmart420_init_data); } else { spca50x_write_vector(spca50x, spca504_pccam600_init_data); } enable = (spca50x->autoexpo ? 0x4 : 0x1); spca50x_reg_write(spca50x->dev, 0x0c, 0x0000, enable); // auto exposure spca50x_reg_write(spca50x->dev, 0xb0, 0x0000, enable); // auto whiteness /* set default exposure compensation and whiteness balance */ spca50x_reg_write(spca50x->dev, 0x30, 0x0001, 800); // ~ 20 fps spca50x_reg_write(spca50x->dev, 0x30, 0x0002, 1600); spca504B_SetSizeType(spca50x); break; } sp5xx_initContBrigHueRegisters(spca50x); } static void sp5xxfw2_stop(struct usb_spca50x *spca50x) { int rc; switch (PWC_SC(spca50x)->pwc_info.bridge) { case BRIDGE_SPCA533: case BRIDGE_SPCA536: case BRIDGE_SPCA504B: usb_wr_vend_dev(spca50x->dev, 0x31, 0, 0, NULL, 0); spca504B_WaitCmdStatus(spca50x); rc = spca504B_PollingDataReady(spca50x->dev); break; case BRIDGE_SPCA504: case BRIDGE_SPCA504C: spca50x_reg_write(spca50x->dev, 0x00, 0x2000, 0x0000); if (spca50x->desc == AiptekMiniPenCam13) { /* spca504a aiptek */ // spca504A_acknowledged_command (spca50x, 0x08, 6, 0, 0x86, 1); spca504A_acknowledged_command(spca50x, 0x24, 0x0000, 0x0000, 0x9d, 1); spca504A_acknowledged_command(spca50x, 0x01, 0x000f, 0x0000, 0xFF, 1); } else { spca504_acknowledged_command(spca50x, 0x24, 0x0000, 0x0000); spca50x_reg_write(spca50x->dev, 0x01, 0x000f, 0x0); } break; } } static __u16 sp5xxfw2_setbrightness(struct usb_spca50x *spca50x) { switch (PWC_SC(spca50x)->pwc_info.bridge) { case BRIDGE_SPCA533: case BRIDGE_SPCA504B: case BRIDGE_SPCA504: case BRIDGE_SPCA504C: spca50x_reg_write(spca50x->dev, 0x0, 0x21a7, (spca50x->brightness >> 8)); break; case BRIDGE_SPCA536: spca50x_reg_write(spca50x->dev, 0x0, 0x20f0, (spca50x->brightness >> 8)); break; } return 0; } static __u16 sp5xxfw2_getbrightness(struct usb_spca50x *spca50x) { __u16 brightness = 0; switch (PWC_SC(spca50x)->pwc_info.bridge) { case BRIDGE_SPCA533: case BRIDGE_SPCA504B: case BRIDGE_SPCA504: case BRIDGE_SPCA504C: brightness = spca50x_reg_read(spca50x->dev, 0x0, 0x21a7, 2); spca50x->brightness = (((brightness & 0xFF) - 128) % 255) << 8; break; case BRIDGE_SPCA536: brightness = spca50x_reg_read(spca50x->dev, 0x0, 0x20f0, 2); spca50x->brightness = (((brightness & 0xFF) - 128) % 255) << 8; break; } return (((brightness & 0xFF) - 128) % 255) << 8; } static __u16 sp5xxfw2_setcontrast(struct usb_spca50x *spca50x) { switch (PWC_SC(spca50x)->pwc_info.bridge) { case BRIDGE_SPCA533: case BRIDGE_SPCA504B: case BRIDGE_SPCA504: case BRIDGE_SPCA504C: spca50x_reg_write(spca50x->dev, 0x0, 0x21a8, spca50x->contrast >> 8); break; case BRIDGE_SPCA536: spca50x_reg_write(spca50x->dev, 0x0, 0x20f1, spca50x->contrast >> 8); break; } return 0; } static __u16 sp5xxfw2_getcontrast(struct usb_spca50x *spca50x) { switch (PWC_SC(spca50x)->pwc_info.bridge) { case BRIDGE_SPCA533: case BRIDGE_SPCA504B: case BRIDGE_SPCA504: case BRIDGE_SPCA504C: spca50x->contrast = spca50x_reg_read(spca50x->dev, 0x0, 0x21a8, 2) << 8; break; case BRIDGE_SPCA536: spca50x->contrast = spca50x_reg_read(spca50x->dev, 0x0, 0x20f1, 2) << 8; break; } return spca50x->contrast; } static __u16 sp5xxfw2_setcolors(struct usb_spca50x *spca50x) { switch (PWC_SC(spca50x)->pwc_info.bridge) { case BRIDGE_SPCA533: case BRIDGE_SPCA504B: case BRIDGE_SPCA504: case BRIDGE_SPCA504C: spca50x_reg_write(spca50x->dev, 0x0, 0x21ae, spca50x->colour >> 8); break; case BRIDGE_SPCA536: spca50x_reg_write(spca50x->dev, 0x0, 0x20f6, spca50x->colour >> 8); break; } return 0; } static __u16 sp5xxfw2_getcolors(struct usb_spca50x *spca50x) { switch (PWC_SC(spca50x)->pwc_info.bridge) { case BRIDGE_SPCA533: case BRIDGE_SPCA504B: case BRIDGE_SPCA504: case BRIDGE_SPCA504C: spca50x->colour = spca50x_reg_read(spca50x->dev, 0x0, 0x21ae, 2) << 7; break; case BRIDGE_SPCA536: spca50x->colour = spca50x_reg_read(spca50x->dev, 0x0, 0x20f6, 2) << 7; break; } return spca50x->colour; } static int sp5xxfw2_config(struct usb_spca50x *spca50x) { switch (PWC_SC(spca50x)->pwc_info.bridge) { case BRIDGE_SPCA504B: case BRIDGE_SPCA504: case BRIDGE_SPCA536: memset(spca50x->mode_cam, 0x00, TOTMODE * sizeof(struct mwebcam)); spca50x->mode_cam[VGA].width = 640; spca50x->mode_cam[VGA].height = 480; spca50x->mode_cam[VGA].t_palette = P_JPEG | P_RAW | P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[VGA].pipe = 1023; spca50x->mode_cam[VGA].method = 0; spca50x->mode_cam[VGA].mode = 1; spca50x->mode_cam[PAL].width = 384; spca50x->mode_cam[PAL].height = 288; spca50x->mode_cam[PAL].t_palette = P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[PAL].pipe = 1023; spca50x->mode_cam[PAL].method = 1; spca50x->mode_cam[PAL].mode = 1; spca50x->mode_cam[SIF].width = 352; spca50x->mode_cam[SIF].height = 288; spca50x->mode_cam[SIF].t_palette = P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[SIF].pipe = 1023; spca50x->mode_cam[SIF].method = 1; spca50x->mode_cam[SIF].mode = 1; spca50x->mode_cam[CIF].width = 320; spca50x->mode_cam[CIF].height = 240; spca50x->mode_cam[CIF].t_palette = P_JPEG | P_RAW | P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[CIF].pipe = 896; spca50x->mode_cam[CIF].method = 0; spca50x->mode_cam[CIF].mode = 2; spca50x->mode_cam[QPAL].width = 192; spca50x->mode_cam[QPAL].height = 144; spca50x->mode_cam[QPAL].t_palette = P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[QPAL].pipe = 896; spca50x->mode_cam[QPAL].method = 1; spca50x->mode_cam[QPAL].mode = 2; spca50x->mode_cam[QSIF].width = 176; spca50x->mode_cam[QSIF].height = 144; spca50x->mode_cam[QSIF].t_palette = P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[QSIF].pipe = 896; spca50x->mode_cam[QSIF].method = 1; spca50x->mode_cam[QSIF].mode = 2; break; case BRIDGE_SPCA533: memset(spca50x->mode_cam, 0x00, TOTMODE * sizeof(struct mwebcam)); spca50x->mode_cam[CUSTOM].width = 464; spca50x->mode_cam[CUSTOM].height = 480; spca50x->mode_cam[CUSTOM].t_palette = P_JPEG | P_RAW | P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[CUSTOM].pipe = 1023; spca50x->mode_cam[CUSTOM].method = 0; spca50x->mode_cam[CUSTOM].mode = 1; spca50x->mode_cam[PAL].width = 384; spca50x->mode_cam[PAL].height = 288; spca50x->mode_cam[PAL].t_palette = P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[PAL].pipe = 1023; spca50x->mode_cam[PAL].method = 1; spca50x->mode_cam[PAL].mode = 1; spca50x->mode_cam[SIF].width = 352; spca50x->mode_cam[SIF].height = 288; spca50x->mode_cam[SIF].t_palette = P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[SIF].pipe = 1023; spca50x->mode_cam[SIF].method = 1; spca50x->mode_cam[SIF].mode = 1; spca50x->mode_cam[CIF].width = 320; spca50x->mode_cam[CIF].height = 240; spca50x->mode_cam[CIF].t_palette = P_JPEG | P_RAW | P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[CIF].pipe = 1023; spca50x->mode_cam[CIF].method = 0; spca50x->mode_cam[CIF].mode = 2; spca50x->mode_cam[QPAL].width = 192; spca50x->mode_cam[QPAL].height = 144; spca50x->mode_cam[QPAL].t_palette = P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[QPAL].pipe = 1023; spca50x->mode_cam[QPAL].method = 1; spca50x->mode_cam[QPAL].mode = 2; spca50x->mode_cam[QSIF].width = 176; spca50x->mode_cam[QSIF].height = 144; spca50x->mode_cam[QSIF].t_palette = P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[QSIF].pipe = 1023; spca50x->mode_cam[QSIF].method = 1; spca50x->mode_cam[QSIF].mode = 2; break; case BRIDGE_SPCA504C: memset(spca50x->mode_cam, 0x00, TOTMODE * sizeof(struct mwebcam)); spca50x->mode_cam[VGA].width = 640; spca50x->mode_cam[VGA].height = 480; spca50x->mode_cam[VGA].t_palette = P_JPEG | P_RAW | P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[VGA].pipe = 1023; spca50x->mode_cam[VGA].method = 0; spca50x->mode_cam[VGA].mode = 1; spca50x->mode_cam[PAL].width = 384; spca50x->mode_cam[PAL].height = 288; spca50x->mode_cam[PAL].t_palette = P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[PAL].pipe = 1023; spca50x->mode_cam[PAL].method = 1; spca50x->mode_cam[PAL].mode = 1; spca50x->mode_cam[SIF].width = 352; spca50x->mode_cam[SIF].height = 288; spca50x->mode_cam[SIF].t_palette = P_JPEG | P_RAW | P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[SIF].pipe = 1023; spca50x->mode_cam[SIF].method = 0; spca50x->mode_cam[SIF].mode = 2; spca50x->mode_cam[CIF].width = 320; spca50x->mode_cam[CIF].height = 240; spca50x->mode_cam[CIF].t_palette = P_JPEG | P_RAW | P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[CIF].pipe = 896; spca50x->mode_cam[CIF].method = 0; spca50x->mode_cam[CIF].mode = 3; spca50x->mode_cam[QPAL].width = 192; spca50x->mode_cam[QPAL].height = 144; spca50x->mode_cam[QPAL].t_palette = P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[QPAL].pipe = 896; spca50x->mode_cam[QPAL].method = 1; spca50x->mode_cam[QPAL].mode = 3; spca50x->mode_cam[QSIF].width = 176; spca50x->mode_cam[QSIF].height = 144; spca50x->mode_cam[QSIF].t_palette = P_JPEG | P_RAW | P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[QSIF].pipe = 768; spca50x->mode_cam[QSIF].method = 0; spca50x->mode_cam[QSIF].mode = 4; break; } spca50x->qindex = 5; return 0; } /****************************************************************************************/ static void spca504B_SetSizeType(struct usb_spca50x *spca50x) { __u8 Size; __u8 Type; int rc; Size = spca50x->mode; Type = 0; switch (PWC_SC(spca50x)->pwc_info.bridge) { case BRIDGE_SPCA533:{ usb_wr_vend_dev(spca50x->dev, 0x31, 0, 0, NULL, 0); spca504B_WaitCmdStatus(spca50x); rc = spca504B_PollingDataReady(spca50x->dev); spca50x_GetFirmware(spca50x); Type = 2; usb_wr_vend_dev(spca50x->dev, 0x24, 0, 8, &Type, 1); usb_rd_vend_dev(spca50x->dev, 0x24, 0, 8, &Type, 1); usb_wr_vend_dev(spca50x->dev, 0x25, 0, 4, &Size, 1); usb_rd_vend_dev(spca50x->dev, 0x25, 0, 4, &Size, 1); rc = spca504B_PollingDataReady(spca50x->dev); /* Init the cam width height with some values get on init ? */ usb_wr_vend_dev(spca50x->dev, 0x31, 0, 4, NULL, 0); spca504B_WaitCmdStatus(spca50x); rc = spca504B_PollingDataReady(spca50x->dev); } break; case BRIDGE_SPCA504B: case BRIDGE_SPCA536: { Type = 6; usb_wr_vend_dev(spca50x->dev, 0x25, 0, 4, &Size, 1); usb_rd_vend_dev(spca50x->dev, 0x25, 0, 4, &Size, 1); usb_wr_vend_dev(spca50x->dev, 0x27, 0, 0, &Type, 1); usb_rd_vend_dev(spca50x->dev, 0x27, 0, 0, &Type, 1); rc = spca504B_PollingDataReady(spca50x->dev); } break; case BRIDGE_SPCA504: Size += 3; if (spca50x->desc == AiptekMiniPenCam13) { /* spca504a aiptek */ spca504A_acknowledged_command(spca50x, 0x8, Size, 0, (0x80 | (Size & 0x0F)), 1); spca504A_acknowledged_command(spca50x, 1, 3, 0, 0x9F, 0); } else { spca504_acknowledged_command(spca50x, 0x8, Size, 0); } break; case BRIDGE_SPCA504C: spca50x_reg_write(spca50x->dev, 0xa0, (0x0500 | (Size & 0x0F)), 0x0); // capture mode spca50x_reg_write(spca50x->dev, 0x20, 0x1, (0x0500 | (Size & 0x0F))); break; } return; } static void spca504_acknowledged_command(struct usb_spca50x *spca50x, __u16 reg, __u16 idx, __u16 val) { __u8 notdone = 0; spca50x_reg_write(spca50x->dev, reg, idx, val); notdone = spca50x_reg_read(spca50x->dev, 0x01, 0x0001, 1); spca50x_reg_write(spca50x->dev, reg, idx, val); PDEBUG(5, "before wait 0x%x", notdone); wait_ms(200); notdone = spca50x_reg_read(spca50x->dev, 0x01, 0x0001, 1); PDEBUG(5, "after wait 0x%x", notdone); return; } static void spca504A_acknowledged_command(struct usb_spca50x *spca50x, __u16 reg, __u16 idx, __u16 val, __u8 stat, __u8 count) { __u8 status; __u8 endcode; spca50x_reg_write(spca50x->dev, reg, idx, val); status = spca50x_reg_read(spca50x->dev, 0x01, 0x0001, 1); endcode = stat; PDEBUG(5, "Status 0x%x Need 0x%x", status, stat); if (count) { while (1) { wait_ms(10); /* gsmart mini2 write a each wait setting 1 ms is enought */ //spca50x_reg_write(spca50x->dev,reg,idx,val); status = spca50x_reg_read(spca50x->dev, 0x01, 0x0001, 1); if (status == endcode) { PDEBUG(5, "status 0x%x after wait 0x%x", status, count); break; } count++; if (count > 200) break; } } return; } static void spca504_wait_status(struct usb_spca50x *spca50x) { int ret = 256; do { /* With this we get the status, when return 0 it's all ok */ ret = spca50x_reg_read(spca50x->dev, 0x06, 0x00, 1); } while (ret--); } static void spca50x_GetFirmware(struct usb_spca50x *spca50x) { __u8 FW[5] = { 0, 0, 0, 0, 0 }; __u8 ProductInfo[64]; usb_rd_vend_dev(spca50x->dev, 0x20, 0, 0, FW, 5); PDEBUG(0, "FirmWare : %d %d %d %d %d ", FW[0], FW[1], FW[2], FW[3], FW[4]); usb_rd_vend_dev(spca50x->dev, 0x23, 0, 0, ProductInfo, 64); usb_rd_vend_dev(spca50x->dev, 0x23, 0, 1, ProductInfo, 64); return; } static int spca504B_PollingDataReady(struct usb_device *dev) { __u8 DataReady = 0; int count = 0; while (1) { usb_rd_vend_dev(dev, 0x21, 0, 0, &DataReady, 1); if ((DataReady & 0x01) == 0) break; wait_ms(10); count++; if (count > 10) break; } return DataReady; } static void spca504B_WaitCmdStatus(struct usb_spca50x *spca50x) { __u8 DataReady = 0; int ReqDone; int count = 0; while (1) { usb_rd_vend_dev(spca50x->dev, 0x21, 0, 1, &DataReady, 1); if (DataReady) { DataReady = 0; usb_wr_vend_dev(spca50x->dev, 0x21, 0, 1, &DataReady, 1); usb_rd_vend_dev(spca50x->dev, 0x21, 0, 1, &DataReady, 1); ReqDone = spca504B_PollingDataReady(spca50x->dev); break; } wait_ms(10); count++; if (count > 50) break; } return; } static void spca504B_setQtable(struct usb_spca50x *spca50x) { __u8 Data = 3; int rc; usb_wr_vend_dev(spca50x->dev, 0x26, 0, 0, &Data, 1); usb_rd_vend_dev(spca50x->dev, 0x26, 0, 0, &Data, 1); rc = spca504B_PollingDataReady(spca50x->dev); return; } static void sp5xx_initContBrigHueRegisters(struct usb_spca50x *spca50x) { int rc; int pollreg = 1; switch (PWC_SC(spca50x)->pwc_info.bridge) { case BRIDGE_SPCA504: case BRIDGE_SPCA504C: pollreg = 0; case BRIDGE_SPCA533: case BRIDGE_SPCA504B: usb_wr_vend_dev(spca50x->dev, 0, 0, 0x21a7, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0, 0x20, 0x21a8, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0, 0, 0x21ad, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0, 1, 0x21ac, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0, 0x20, 0x21ae, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0, 0, 0x21a3, NULL, 0); break; case BRIDGE_SPCA536: usb_wr_vend_dev(spca50x->dev, 0, 0, 0x20f0, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0, 0x21, 0x20f1, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0, 0x40, 0x20f5, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0, 1, 0x20f4, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0, 0x40, 0x20f6, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0, 0, 0x2089, NULL, 0); break; } if (pollreg) rc = spca504B_PollingDataReady(spca50x->dev); return; } #endif //SP5XXFW2 pwcbsd/spca5xx-20060402/drivers/usb/spca500_init.h000744 000423 000000 00000013053 10546676142 021720 0ustar00luigiwheel000000 000000 /* * SPCA500 chip based cameras initialization data * */ #ifndef SPCA500_INIT_H #define SPCA500_INIT_H /* Frame packet header offsets for the spca500 */ #define SPCA500_OFFSET_PADDINGLB 2 #define SPCA500_OFFSET_PADDINGHB 3 #define SPCA500_OFFSET_MODE 4 #define SPCA500_OFFSET_IMGWIDTH 5 #define SPCA500_OFFSET_IMGHEIGHT 6 #define SPCA500_OFFSET_IMGMODE 7 #define SPCA500_OFFSET_QTBLINDEX 8 #define SPCA500_OFFSET_FRAMSEQ 9 #define SPCA500_OFFSET_CDSPINFO 10 #define SPCA500_OFFSET_GPIO 11 #define SPCA500_OFFSET_AUGPIO 12 #define SPCA500_OFFSET_DATA 16 static void spca500_clksmart310_init(struct usb_spca50x *spca50x); void spca500_reinit(struct usb_spca50x *spca50x); static int spca500_initialise(struct usb_spca50x *spca50x); static int spca500_full_reset(struct usb_spca50x *spca50x); #if 0 static __u16 spca500_read_stats[][3] = { {0x0c, 0x0000, 0x0000}, {0x30, 0x03fd, 0x0001}, /* possible values for following call: 0x01b3, 0x01e6, 0x01f7, 0x0218 */ {0x30, 0x01b3, 0x0002}, /* possible values for following call: 0x0000, 0x0001, 0x0002 */ {0x30, 0x0000, 0x0003}, {0x30, 0x003b, 0x0004}, /* possible values for following call: 0x00aa, 0x00e0 */ {0x30, 0x00e0, 0x0005}, {0x30, 0x0001, 0x0006}, {0x30, 0x0080, 0x0007}, {0x30, 0x0004, 0x0000}, {0, 0, 0} }; #endif static __u16 spca500_visual_defaults[][3] = { {0x00, 0x0003, 0x816b}, /* SSI not active sync with vsync, * hue (H byte) = 0, * saturation/hue enable, * brightness/contrast enable. */ {0x00, 0x0000, 0x8167}, /* brightness = 0 */ {0x00, 0x0020, 0x8168}, /* contrast = 0 */ {0x00, 0x0003, 0x816b}, /* SSI not active sync with vsync, * hue (H byte) = 0, saturation/hue enable, * brightness/contrast enable. * was 0x0003, now 0x0000. */ {0x00, 0x0000, 0x816a}, /* hue (L byte) = 0 */ {0x00, 0x0020, 0x8169}, /* saturation = 0x20 */ {0x00, 0x0050, 0x8157}, /* edge gain high threshold */ {0x00, 0x0030, 0x8158}, /* edge gain low threshold */ {0x00, 0x0028, 0x8159}, /* edge bandwidth high threshold */ {0x00, 0x000a, 0x815a}, /* edge bandwidth low threshold */ {0x00, 0x0001, 0x8202}, /* clock rate compensation = 1/25 sec/frame */ {0x0c, 0x0004, 0x0000}, /* set interface */ {0, 0, 0} }; static __u16 Clicksmart510_defaults[][3] = { {0x00, 0x00, 0x8211}, {0x00, 0x01, 0x82c0}, {0x00, 0x10, 0x82cb}, {0x00, 0x0f, 0x800d}, {0x00, 0x82, 0x8225}, {0x00, 0x21, 0x8228}, {0x00, 0x00, 0x8203}, {0x00, 0x00, 0x8204}, {0x00, 0x08, 0x8205}, {0x00, 0xf8, 0x8206}, {0x00, 0x28, 0x8207}, {0x00, 0xa0, 0x8208}, {0x00, 0x08, 0x824a}, {0x00, 0x08, 0x8214}, {0x00, 0x80, 0x82c1}, {0x00, 0x00, 0x82c2}, {0x00, 0x00, 0x82ca}, {0x00, 0x80, 0x82c1}, {0x00, 0x04, 0x82c2}, {0x00, 0x00, 0x82ca}, {0x00, 0xfc, 0x8100}, {0x00, 0xfc, 0x8105}, {0x00, 0x30, 0x8101}, {0x00, 0x00, 0x8102}, {0x00, 0x00, 0x8103}, {0x00, 0x66, 0x8107}, {0x00, 0x00, 0x816b}, {0x00, 0x00, 0x8155}, {0x00, 0x01, 0x8156}, {0x00, 0x60, 0x8157}, {0x00, 0x40, 0x8158}, {0x00, 0x0a, 0x8159}, {0x00, 0x06, 0x815a}, {0x00, 0x00, 0x813f}, {0x00, 0x00, 0x8200}, {0x00, 0x19, 0x8201}, {0x00, 0x00, 0x82c1}, {0x00, 0xa0, 0x82c2}, {0x00, 0x00, 0x82ca}, {0x00, 0x00, 0x8117}, {0x00, 0x00, 0x8118}, {0x00, 0x65, 0x8119}, {0x00, 0x00, 0x811a}, {0x00, 0x00, 0x811b}, {0x00, 0x55, 0x811c}, {0x00, 0x65, 0x811d}, {0x00, 0x55, 0x811e}, {0x00, 0x16, 0x811f}, {0x00, 0x19, 0x8120}, {0x00, 0x80, 0x8103}, {0x00, 0x83, 0x816b}, {0x00, 0x25, 0x8168}, {0x00, 0x01, 0x820f}, {0x00, 0xff, 0x8115}, {0x00, 0x48, 0x8116}, {0x00, 0x50, 0x8151}, {0x00, 0x40, 0x8152}, {0x00, 0x78, 0x8153}, {0x00, 0x40, 0x8154}, {0x00, 0x00, 0x8167}, {0x00, 0x20, 0x8168}, {0x00, 0x00, 0x816a}, {0x00, 0x03, 0x816b}, {0x00, 0x20, 0x8169}, {0x00, 0x60, 0x8157}, {0x00, 0x00, 0x8190}, {0x00, 0x00, 0x81a1}, {0x00, 0x00, 0x81b2}, {0x00, 0x27, 0x8191}, {0x00, 0x27, 0x81a2}, {0x00, 0x27, 0x81b3}, {0x00, 0x4b, 0x8192}, {0x00, 0x4b, 0x81a3}, {0x00, 0x4b, 0x81b4}, {0x00, 0x66, 0x8193}, {0x00, 0x66, 0x81a4}, {0x00, 0x66, 0x81b5}, {0x00, 0x79, 0x8194}, {0x00, 0x79, 0x81a5}, {0x00, 0x79, 0x81b6}, {0x00, 0x8a, 0x8195}, {0x00, 0x8a, 0x81a6}, {0x00, 0x8a, 0x81b7}, {0x00, 0x9b, 0x8196}, {0x00, 0x9b, 0x81a7}, {0x00, 0x9b, 0x81b8}, {0x00, 0xa6, 0x8197}, {0x00, 0xa6, 0x81a8}, {0x00, 0xa6, 0x81b9}, {0x00, 0xb2, 0x8198}, {0x00, 0xb2, 0x81a9}, {0x00, 0xb2, 0x81ba}, {0x00, 0xbe, 0x8199}, {0x00, 0xbe, 0x81aa}, {0x00, 0xbe, 0x81bb}, {0x00, 0xc8, 0x819a}, {0x00, 0xc8, 0x81ab}, {0x00, 0xc8, 0x81bc}, {0x00, 0xd2, 0x819b}, {0x00, 0xd2, 0x81ac}, {0x00, 0xd2, 0x81bd}, {0x00, 0xdb, 0x819c}, {0x00, 0xdb, 0x81ad}, {0x00, 0xdb, 0x81be}, {0x00, 0xe4, 0x819d}, {0x00, 0xe4, 0x81ae}, {0x00, 0xe4, 0x81bf}, {0x00, 0xed, 0x819e}, {0x00, 0xed, 0x81af}, {0x00, 0xed, 0x81c0}, {0x00, 0xf7, 0x819f}, {0x00, 0xf7, 0x81b0}, {0x00, 0xf7, 0x81c1}, {0x00, 0xff, 0x81a0}, {0x00, 0xff, 0x81b1}, {0x00, 0xff, 0x81c2}, {0x00, 0x03, 0x8156}, {0x00, 0x00, 0x8211}, {0x00, 0x20, 0x8168}, {0x00, 0x01, 0x8202}, {0x00, 0x30, 0x8101}, {0x00, 0x00, 0x8111}, {0x00, 0x00, 0x8112}, {0x00, 0x00, 0x8113}, {0x00, 0x00, 0x8114}, {0, 0, 0} }; #endif /* SPCA500_INIT_H */ pwcbsd/spca5xx-20060402/drivers/usb/spca501_init.h000755 000423 000000 00000152175 10553461760 021730 0ustar00luigiwheel000000 000000 /* * SPCA501 chip based cameras initialization data * */ #ifndef SPCA501_INIT_H #define SPCA501_INIT_H /* * Data to initialize a SPCA501. From a capture file provided by Bill Roehl * With SPCA501 chip description */ #define CCDSP_SET // set CCDSP parameters #define TG_SET // set time generator set #undef DSPWIN_SET // set DSP windows parameters #undef ALTER_GAMA // Set alternate set to YUV transform coeffs. #define SPCA501_SNAPBIT 0x80 #define SPCA501_SNAPCTRL 0x10 /* Frame packet header offsets for the spca501 */ #define SPCA501_OFFSET_GPIO 1 #define SPCA501_OFFSET_TYPE 2 #define SPCA501_OFFSET_TURN3A 3 #define SPCA501_OFFSET_FRAMSEQ 4 #define SPCA501_OFFSET_COMPRESS 5 #define SPCA501_OFFSET_QUANT 6 #define SPCA501_OFFSET_QUANT2 7 #define SPCA501_OFFSET_DATA 8 #define SPCA501_PROP_COMP_ENABLE(d) ( (d) & 1 ) #define SPCA501_PROP_SNAP(d) ( (d) & 0x40 ) #define SPCA501_PROP_SNAP_CTRL(d) ( (d) & 0x10) #define SPCA501_PROP_COMP_THRESH(d) ( ((d) & 0xE ) >> 1) #define SPCA501_PROP_COMP_QUANT(d) ( ((d) & 0x70 ) >> 4) /* SPCA501 CCDSP control */ #define SPCA501_REG_CCDSP 0x1 /* SPCA501 control/status registers */ #define SPCA501_REG_CTLRL 0x2 //registers for color correction and YUV transformation #define SPCA501_A11 0x08 #define SPCA501_A12 0x09 #define SPCA501_A13 0x0A #define SPCA501_A21 0x0B #define SPCA501_A22 0x0C #define SPCA501_A23 0x0D #define SPCA501_A31 0x0E #define SPCA501_A32 0x0F #define SPCA501_A33 0x10 /* Data for video camera initialization before capturing */ static __u16 spca501_open_data[][3] = { /* bmRequest,value,index */ {0x2, 0x50, 0x0}, //C/S enable soft reset {0x2, 0x40, 0x0}, //C/S disable soft reset {0x2, 0x02, 0x5}, //C/S general purpose I/O data {0x2, 0x03, 0x5}, //C/S general purpose I/O data #ifdef CCDSP_SET {0x1, 0x38, 0x1}, // CCDSP options {0x1, 0x05, 0x2}, // CCDSP Optical black level for user settings {0x1, 0xC0, 0x3}, // CCDSP Optical black settings {0x1, 0x67, 0x7}, {0x1, 0x63, 0x3f}, // CCDSP CCD gamma enable {0x1, 0x03, 0x56}, // Add gamma correction {0x1, 0xFF, 0x15}, //CCDSP High luminance for white balance {0x1, 0x01, 0x16}, //CCDSP Low luminance for white balance /* Color correction and RGB-to-YUV transformation coefficients changing */ #ifdef ALTER_GAMA {0x0, 0x00, 0x08}, //A11 {0x0, 0x00, 0x09}, //A12 {0x0, 0x90, 0x0A}, //A13 {0x0, 0x12, 0x0B}, //A21 {0x0, 0x00, 0x0C}, //A22 {0x0, 0x00, 0x0D}, //A23 {0x0, 0x00, 0x0E}, //A31 {0x0, 0x02, 0x0F}, //A32 {0x0, 0x00, 0x10}, //A33 #else {0x1, 0x31, 0x08}, //A11 {0x1, 0x00, 0x09}, //A12 {0x1, 0x00, 0x0A}, //A13 {0x1, 0x00, 0x0B}, //A21 {0x1, 0x14, 0x0C}, //A22 {0x1, 0x00, 0x0D}, //A23 {0x1, 0x00, 0x0E}, //A31 {0x1, 0x00, 0x0F}, //A32 {0x1, 0x1E, 0x10}, //A33 #endif {0x1, 0x00, 0x11}, // R offset {0x1, 0x00, 0x12}, // G offset {0x1, 0x00, 0x13}, // B offset {0x1, 0x00, 0x14}, // GB offset #endif #ifdef TG_SET /* Time generator manipulations */ {0x0, 0xfc, 0x0}, // Set up high bits of shutter speed {0x0, 0x01, 0x1}, // Set up low bits of shutter speed {0x0, 0xe4, 0x04}, // DCLK*2 clock phase adjustment {0x0, 0x08, 0x05}, // ADCK phase adjustment, inv. ext. VB {0x0, 0x03, 0x06}, // FR phase adjustment {0x0, 0x01, 0x07}, // FCDS phase adjustment {0x0, 0x39, 0x08}, // FS phase adjustment {0x0, 0x88, 0x0a}, // FH1 phase and delay adjustment {0x0, 0x03, 0x0f}, // pixel identification {0x0, 0x00, 0x11}, // clock source selection (default) /*VERY strange manipulations with * select DMCLP or OBPX to be ADCLP output (0x0C) * OPB always toggle or not (0x0D) but they allow * us to set up brightness */ {0x0, 0x01, 0x0c}, {0x0, 0xe0, 0x0d}, /* Done */ #endif #ifdef DSPWIN_SET {0x1, 0xa0, 0x01}, //Setting image processing parameters {0x1, 0x1c, 0x17}, //Changing Windows positions X1 {0x1, 0xe2, 0x19}, //X2 {0x1, 0x1c, 0x1b}, //X3 {0x1, 0xe2, 0x1d}, //X4 {0x1, 0x5f, 0x1f}, //X5 {0x1, 0x32, 0x20}, //Y5 {0x1, 0x01, 0x10}, //Changing A33 #endif {0x2, 0x204a, 0x07}, //Setting video compression & resolution 160x120 {0x2, 0x94, 0x06}, //Setting video no compression {0, 0, 0} }; /* The SPCAxxx docs from Sunplus document these values in tables, one table per register number. In the data below, dmRequest is the register number, index is the Addr, and value is a combination of Bit values. Bit Value (hex) 0 01 1 02 2 04 3 08 4 10 5 20 6 40 7 80 */ /* Data for chip initialization (set default values) */ static __u16 spca501_init_data[][3] = { /* Set all the values to powerup defaults */ /* bmRequest,value,index */ {0x0, 0xAA, 0x00}, {0x0, 0x02, 0x01}, {0x0, 0x01, 0x02}, {0x0, 0x02, 0x03}, {0x0, 0xCE, 0x04}, {0x0, 0x00, 0x05}, {0x0, 0x00, 0x06}, {0x0, 0x00, 0x07}, {0x0, 0x00, 0x08}, {0x0, 0x00, 0x09}, {0x0, 0x90, 0x0A}, {0x0, 0x12, 0x0B}, {0x0, 0x00, 0x0C}, {0x0, 0x00, 0x0D}, {0x0, 0x00, 0x0E}, {0x0, 0x02, 0x0F}, {0x0, 0x00, 0x10}, {0x0, 0x00, 0x11}, {0x0, 0x00, 0x12}, {0x0, 0x00, 0x13}, {0x0, 0x00, 0x14}, {0x0, 0x00, 0x15}, {0x0, 0x00, 0x16}, {0x0, 0x00, 0x17}, {0x0, 0x00, 0x18}, {0x0, 0x00, 0x19}, {0x0, 0x00, 0x1A}, {0x0, 0x00, 0x1B}, {0x0, 0x00, 0x1C}, {0x0, 0x00, 0x1D}, {0x0, 0x00, 0x1E}, {0x0, 0x00, 0x1F}, {0x0, 0x00, 0x20}, {0x0, 0x00, 0x21}, {0x0, 0x00, 0x22}, {0x0, 0x00, 0x23}, {0x0, 0x00, 0x24}, {0x0, 0x00, 0x25}, {0x0, 0x00, 0x26}, {0x0, 0x00, 0x27}, {0x0, 0x00, 0x28}, {0x0, 0x00, 0x29}, {0x0, 0x00, 0x2A}, {0x0, 0x00, 0x2B}, {0x0, 0x00, 0x2C}, {0x0, 0x00, 0x2D}, {0x0, 0x00, 0x2E}, {0x0, 0x00, 0x2F}, {0x0, 0x00, 0x30}, {0x0, 0x00, 0x31}, {0x0, 0x00, 0x32}, {0x0, 0x00, 0x33}, {0x0, 0x00, 0x34}, {0x0, 0x00, 0x35}, {0x0, 0x00, 0x36}, {0x0, 0x00, 0x37}, {0x0, 0x00, 0x38}, {0x0, 0x00, 0x39}, {0x0, 0x00, 0x3A}, {0x0, 0x00, 0x3B}, {0x0, 0x00, 0x3C}, {0x0, 0x00, 0x3D}, {0x0, 0x00, 0x3E}, {0x0, 0x00, 0x3F}, {0x0, 0x00, 0x40}, {0x0, 0x00, 0x41}, {0x0, 0x00, 0x42}, {0x0, 0x00, 0x43}, {0x0, 0x00, 0x44}, {0x0, 0x00, 0x45}, {0x0, 0x00, 0x46}, {0x0, 0x00, 0x47}, {0x0, 0x00, 0x48}, {0x0, 0x00, 0x49}, {0x0, 0x00, 0x4A}, {0x0, 0x00, 0x4B}, {0x0, 0x00, 0x4C}, {0x0, 0x00, 0x4D}, {0x0, 0x00, 0x4E}, {0x0, 0x00, 0x4F}, {0x0, 0x00, 0x50}, {0x0, 0x00, 0x51}, {0x0, 0x00, 0x52}, {0x0, 0x00, 0x53}, {0x0, 0x00, 0x54}, {0x0, 0x00, 0x55}, {0x0, 0x00, 0x56}, {0x0, 0x00, 0x57}, {0x0, 0x00, 0x58}, {0x0, 0x00, 0x59}, {0x0, 0x00, 0x5A}, {0x0, 0x00, 0x5B}, {0x0, 0x00, 0x5C}, {0x0, 0x00, 0x5D}, {0x0, 0x00, 0x5E}, {0x0, 0x00, 0x5F}, {0x0, 0x00, 0x60}, {0x0, 0x00, 0x61}, {0x0, 0x00, 0x62}, {0x0, 0x00, 0x63}, {0x0, 0x00, 0x64}, {0x0, 0x00, 0x65}, {0x0, 0x00, 0x66}, {0x0, 0x00, 0x67}, {0x0, 0x00, 0x68}, {0x0, 0x00, 0x69}, {0x0, 0x00, 0x6A}, {0x0, 0x00, 0x6B}, {0x0, 0x00, 0x6C}, {0x0, 0x00, 0x6D}, {0x0, 0x00, 0x6E}, {0x0, 0x00, 0x6F}, {0x0, 0x00, 0x70}, {0x0, 0x00, 0x71}, {0x0, 0x00, 0x72}, {0x0, 0x00, 0x73}, {0x0, 0x00, 0x74}, {0x0, 0x00, 0x75}, {0x0, 0x00, 0x76}, {0x0, 0x00, 0x77}, {0x0, 0x00, 0x78}, {0x0, 0x00, 0x79}, {0x0, 0x00, 0x7A}, {0x0, 0x00, 0x7B}, {0x0, 0x00, 0x7C}, {0x0, 0x00, 0x7D}, {0x0, 0x00, 0x7E}, {0x0, 0x00, 0x7F}, {0x0, 0x00, 0x80}, {0x0, 0x00, 0x81}, {0x0, 0x00, 0x82}, {0x0, 0x00, 0x83}, {0x0, 0x00, 0x84}, {0x0, 0x00, 0x85}, {0x0, 0x00, 0x86}, {0x0, 0x00, 0x87}, {0x0, 0x00, 0x88}, {0x0, 0x00, 0x89}, {0x0, 0x00, 0x8A}, {0x0, 0x00, 0x8B}, {0x0, 0x00, 0x8C}, {0x0, 0x00, 0x8D}, {0x0, 0x00, 0x8E}, {0x0, 0x00, 0x8F}, {0x0, 0x00, 0x90}, {0x0, 0x00, 0x91}, {0x0, 0x00, 0x92}, {0x0, 0x00, 0x93}, {0x0, 0x00, 0x94}, {0x0, 0x00, 0x95}, {0x0, 0x00, 0x96}, {0x0, 0x00, 0x97}, {0x0, 0x00, 0x98}, {0x0, 0x00, 0x99}, {0x0, 0x00, 0x9A}, {0x0, 0x00, 0x9B}, {0x0, 0x00, 0x9C}, {0x0, 0x00, 0x9D}, {0x0, 0x00, 0x9E}, {0x0, 0x00, 0x9F}, {0x0, 0x00, 0xA0}, {0x0, 0x00, 0xA1}, {0x0, 0x00, 0xA2}, {0x0, 0x00, 0xA3}, {0x0, 0x00, 0xA4}, {0x0, 0x00, 0xA5}, {0x0, 0x00, 0xA6}, {0x0, 0x00, 0xA7}, {0x0, 0x00, 0xA8}, {0x0, 0x00, 0xA9}, {0x0, 0x00, 0xAA}, {0x0, 0x00, 0xAB}, {0x0, 0x00, 0xAC}, {0x0, 0x00, 0xAD}, {0x0, 0x00, 0xAE}, {0x0, 0x00, 0xAF}, {0x0, 0x00, 0xB0}, {0x0, 0x00, 0xB1}, {0x0, 0x00, 0xB2}, {0x0, 0x00, 0xB3}, {0x0, 0x00, 0xB4}, {0x0, 0x00, 0xB5}, {0x0, 0x00, 0xB6}, {0x0, 0x00, 0xB7}, {0x0, 0x00, 0xB8}, {0x0, 0x00, 0xB9}, {0x0, 0x00, 0xBA}, {0x0, 0x00, 0xBB}, {0x0, 0x00, 0xBC}, {0x0, 0x00, 0xBD}, {0x0, 0x00, 0xBE}, {0x0, 0x00, 0xBF}, {0x0, 0x00, 0xC0}, {0x0, 0x00, 0xC1}, {0x0, 0x00, 0xC2}, {0x0, 0x00, 0xC3}, {0x0, 0x00, 0xC4}, {0x0, 0x00, 0xC5}, {0x0, 0x00, 0xC6}, {0x0, 0x00, 0xC7}, {0x0, 0x00, 0xC8}, {0x0, 0x00, 0xC9}, {0x0, 0x00, 0xCA}, {0x0, 0x00, 0xCB}, {0x0, 0x00, 0xCC}, {0x1, 0xF4, 0x00}, {0x1, 0x38, 0x01}, {0x1, 0x40, 0x02}, {0x1, 0x0A, 0x03}, {0x1, 0x40, 0x04}, {0x1, 0x40, 0x05}, {0x1, 0x40, 0x06}, {0x1, 0x67, 0x07}, {0x1, 0x31, 0x08}, {0x1, 0x00, 0x09}, {0x1, 0x00, 0x0A}, {0x1, 0x00, 0x0B}, {0x1, 0x14, 0x0C}, {0x1, 0x00, 0x0D}, {0x1, 0x00, 0x0E}, {0x1, 0x00, 0x0F}, {0x1, 0x1E, 0x10}, {0x1, 0x00, 0x11}, {0x1, 0x00, 0x12}, {0x1, 0x00, 0x13}, {0x1, 0x00, 0x14}, {0x1, 0xFF, 0x15}, {0x1, 0x01, 0x16}, {0x1, 0x32, 0x17}, {0x1, 0x23, 0x18}, {0x1, 0xCE, 0x19}, {0x1, 0x23, 0x1A}, {0x1, 0x32, 0x1B}, {0x1, 0x8D, 0x1C}, {0x1, 0xCE, 0x1D}, {0x1, 0x8D, 0x1E}, {0x1, 0x00, 0x1F}, {0x1, 0x00, 0x20}, {0x1, 0xFF, 0x3E}, {0x1, 0x02, 0x3F}, {0x1, 0x00, 0x40}, {0x1, 0x00, 0x41}, {0x1, 0x00, 0x42}, {0x1, 0x00, 0x43}, {0x1, 0x00, 0x44}, {0x1, 0x00, 0x45}, {0x1, 0x00, 0x46}, {0x1, 0x00, 0x47}, {0x1, 0x00, 0x48}, {0x1, 0x00, 0x49}, {0x1, 0x00, 0x4A}, {0x1, 0x00, 0x4B}, {0x1, 0x00, 0x4C}, {0x1, 0x00, 0x4D}, {0x1, 0x00, 0x4E}, {0x1, 0x00, 0x4F}, {0x1, 0x00, 0x50}, {0x1, 0x00, 0x51}, {0x1, 0x00, 0x52}, {0x1, 0x00, 0x53}, {0x1, 0x00, 0x54}, {0x1, 0x00, 0x55}, {0x1, 0x00, 0x56}, {0x1, 0x00, 0x57}, {0x1, 0x00, 0x58}, {0x1, 0x00, 0x59}, {0x1, 0x00, 0x5A}, {0x2, 0x03, 0x00}, {0x2, 0x00, 0x01}, {0x2, 0x00, 0x05}, {0x2, 0x00, 0x06}, {0x2, 0x00, 0x07}, {0x2, 0x00, 0x10}, {0x2, 0x00, 0x11}, /* Strange - looks like the 501 driver doesn't do anything * at insert time except read the EEPROM */ {0x0, 0x0, 0x0} }; /* Data for video camera init before capture. * Capture and decoding by Colin Peart. * This is is for the 3com HomeConnect Lite which is spca501a based. */ static __u16 spca501_3com_open_data[][3] = { /* bmRequest,value,index */ {0x2, 0x0050, 0x0000}, //C/S Enable TG soft reset, timing mode=010 {0x2, 0x0043, 0x0000}, //C/S Disable TG soft reset, timing mode=010 {0x2, 0x0002, 0x0005}, //C/S GPIO {0x2, 0x0003, 0x0005}, //C/S GPIO #ifdef CCDSP_SET {0x1, 0x0020, 0x0001}, //CCDSP Options {0x1, 0x0020, 0x0002}, //CCDSP Black Level {0x1, 0x006e, 0x0007}, //CCDSP Gamma options {0x1, 0x0090, 0x0015}, //CCDSP Luminance Low {0x1, 0x00ff, 0x0016}, //CCDSP Luminance High {0x1, 0x0003, 0x003F}, //CCDSP Gamma correction toggle #ifdef ALTER_GAMMA {0x1, 0x0010, 0x0008}, //CCDSP YUV A11 {0x1, 0x0000, 0x0009}, //CCDSP YUV A12 {0x1, 0x0000, 0x000a}, //CCDSP YUV A13 {0x1, 0x0000, 0x000b}, //CCDSP YUV A21 {0x1, 0x0010, 0x000c}, //CCDSP YUV A22 {0x1, 0x0000, 0x000d}, //CCDSP YUV A23 {0x1, 0x0000, 0x000e}, //CCDSP YUV A31 {0x1, 0x0000, 0x000f}, //CCDSP YUV A32 {0x1, 0x0010, 0x0010}, //CCDSP YUV A33 {0x1, 0x0000, 0x0011}, //CCDSP R Offset {0x1, 0x0000, 0x0012}, //CCDSP G Offset {0x1, 0x0001, 0x0013}, //CCDSP B Offset {0x1, 0x0001, 0x0014}, //CCDSP BG Offset {0x1, 0x003f, 0x00C1}, //CCDSP Gamma Correction Enable #endif #endif #ifdef TG_SET {0x0, 0x00fc, 0x0000}, //TG Shutter Speed High Bits {0x0, 0x0000, 0x0001}, //TG Shutter Speed Low Bits {0x0, 0x00e4, 0x0004}, //TG DCLK*2 Adjust {0x0, 0x0008, 0x0005}, //TG ADCK Adjust {0x0, 0x0003, 0x0006}, //TG FR Phase Adjust {0x0, 0x0001, 0x0007}, //TG FCDS Phase Adjust {0x0, 0x0039, 0x0008}, //TG FS Phase Adjust {0x0, 0x0088, 0x000a}, //TG MH1 {0x0, 0x0003, 0x000f}, //TG Pixel ID /* Like below, unexplained toglleing */ {0x0, 0x0080, 0x000c}, {0x0, 0x0000, 0x000d}, {0x0, 0x0080, 0x000c}, {0x0, 0x0004, 0x000d}, {0x0, 0x0000, 0x000c}, {0x0, 0x0000, 0x000d}, {0x0, 0x0040, 0x000c}, {0x0, 0x0017, 0x000d}, {0x0, 0x00c0, 0x000c}, {0x0, 0x0000, 0x000d}, {0x0, 0x0080, 0x000c}, {0x0, 0x0006, 0x000d}, {0x0, 0x0080, 0x000c}, {0x0, 0x0004, 0x000d}, {0x0, 0x0002, 0x0003}, #endif #ifdef DSPWIN_SET {0x1, 0x001c, 0x0017}, //CCDSP W1 Start X {0x1, 0x00e2, 0x0019}, //CCDSP W2 Start X {0x1, 0x001c, 0x001b}, //CCDSP W3 Start X {0x1, 0x00e2, 0x001d}, //CCDSP W4 Start X {0x1, 0x00aa, 0x001f}, //CCDSP W5 Start X {0x1, 0x0070, 0x0020}, //CCDSP W5 Start Y #endif {0x0, 0x0001, 0x0010}, //TG Start Clock //{0x2, 0x006a, 0x0001}, //C/S Enable ISOSYNCH Packet Engine {0x2, 0x0068, 0x0001}, //C/S Diable ISOSYNCH Packet Engine {0x2, 0x0000, 0x0005}, {0x2, 0x0043, 0x0000}, //C/S Set Timing Mode, Disable TG soft reset {0x2, 0x0043, 0x0000}, //C/S Set Timing Mode, Disable TG soft reset {0x2, 0x0002, 0x0005}, //C/S GPIO {0x2, 0x0003, 0x0005}, //C/S GPIO {0x2, 0x006a, 0x0001}, //C/S Enable ISOSYNCH Packet Engine {0, 0, 0} }; /* * Data used to initialize a SPCA501C with HV7131B sensor. * From a capture file taken with USBSnoop v 1.5 * I have a "SPCA501C pc camera chipset" manual by sunplus, but some * of the value meanings are obscure or simply "reserved". * to do list: * 1) Understand what every value means * 2) Understand why some values seem to appear more than once * 3) Write a small comment for each line of the following arrays. */ static __u16 spca501c_arowana_open_data[][3] = { /* bmRequest,value,index */ {0x02, 0x0007, 0x0005}, {0x02, 0xa048, 0x0000}, {0x05, 0x0022, 0x0004}, {0x01, 0x0006, 0x0011}, {0x01, 0x00ff, 0x0012}, {0x01, 0x0014, 0x0013}, {0x01, 0x0000, 0x0014}, {0x01, 0x0042, 0x0051}, {0x01, 0x0040, 0x0052}, {0x01, 0x0051, 0x0053}, {0x01, 0x0040, 0x0054}, {0x01, 0x0000, 0x0055}, {0x00, 0x0025, 0x0000}, {0x00, 0x0026, 0x0000}, {0x00, 0x0001, 0x0000}, {0x00, 0x0027, 0x0000}, {0x00, 0x008a, 0x0000}, {0x00, 0x0000, 0x0000} }; static __u16 spca501c_arowana_init_data[][3] = { /* bmRequest,value,index */ {0x02, 0x0007, 0x0005}, {0x02, 0xa048, 0x0000}, {0x05, 0x0022, 0x0004}, {0x01, 0x0006, 0x0011}, {0x01, 0x00ff, 0x0012}, {0x01, 0x0014, 0x0013}, {0x01, 0x0000, 0x0014}, {0x01, 0x0042, 0x0051}, {0x01, 0x0040, 0x0052}, {0x01, 0x0051, 0x0053}, {0x01, 0x0040, 0x0054}, {0x01, 0x0000, 0x0055}, {0x00, 0x0025, 0x0000}, {0x00, 0x0026, 0x0000}, {0x00, 0x0001, 0x0000}, {0x00, 0x0027, 0x0000}, {0x00, 0x008a, 0x0000}, {0x02, 0x0000, 0x0005}, {0x02, 0x0007, 0x0005}, {0x02, 0x2000, 0x0000}, {0x05, 0x0022, 0x0004}, {0x05, 0x0015, 0x0001}, {0x05, 0x00ea, 0x0000}, {0x05, 0x0021, 0x0001}, {0x05, 0x00d2, 0x0000}, {0x05, 0x0023, 0x0001}, {0x05, 0x0003, 0x0000}, {0x05, 0x0030, 0x0001}, {0x05, 0x002b, 0x0000}, {0x05, 0x0031, 0x0001}, {0x05, 0x0023, 0x0000}, {0x05, 0x0032, 0x0001}, {0x05, 0x0023, 0x0000}, {0x05, 0x0033, 0x0001}, {0x05, 0x0023, 0x0000}, {0x05, 0x0034, 0x0001}, {0x05, 0x0002, 0x0000}, {0x05, 0x0050, 0x0001}, {0x05, 0x0000, 0x0000}, {0x05, 0x0051, 0x0001}, {0x05, 0x0000, 0x0000}, {0x05, 0x0052, 0x0001}, {0x05, 0x0000, 0x0000}, {0x05, 0x0054, 0x0001}, {0x05, 0x0001, 0x0000}, {0x00, 0x0000, 0x0001}, {0x00, 0x0000, 0x0002}, {0x00, 0x000c, 0x0003}, {0x00, 0x0000, 0x0004}, {0x00, 0x0090, 0x0005}, {0x00, 0x0000, 0x0006}, {0x00, 0x0040, 0x0007}, {0x00, 0x00c0, 0x0008}, {0x00, 0x004a, 0x0009}, {0x00, 0x0000, 0x000a}, {0x00, 0x0000, 0x000b}, {0x00, 0x0001, 0x000c}, {0x00, 0x0001, 0x000d}, {0x00, 0x0000, 0x000e}, {0x00, 0x0002, 0x000f}, {0x00, 0x0001, 0x0010}, {0x00, 0x0000, 0x0011}, {0x00, 0x0000, 0x0012}, {0x00, 0x0002, 0x0020}, {0x00, 0x0080, 0x0021}, {0x00, 0x0001, 0x0022}, {0x00, 0x00e0, 0x0023}, {0x00, 0x0000, 0x0024}, {0x00, 0x00d5, 0x0025}, {0x00, 0x0000, 0x0026}, {0x00, 0x000b, 0x0027}, {0x00, 0x0000, 0x0046}, {0x00, 0x0000, 0x0047}, {0x00, 0x0000, 0x0048}, {0x00, 0x0000, 0x0049}, {0x00, 0x0008, 0x004a}, {0xff, 0x0000, 0x00d0}, {0xff, 0x00d8, 0x00d1}, {0xff, 0x0000, 0x00d4}, {0xff, 0x0000, 0x00d5}, {0x01, 0x00a6, 0x0000}, {0x01, 0x0028, 0x0001}, {0x01, 0x0000, 0x0002}, {0x01, 0x000a, 0x0003}, {0x01, 0x0040, 0x0004}, {0x01, 0x0066, 0x0007}, {0x01, 0x0011, 0x0008}, {0x01, 0x0032, 0x0009}, {0x01, 0x00fd, 0x000a}, {0x01, 0x0038, 0x000b}, {0x01, 0x00d1, 0x000c}, {0x01, 0x00f7, 0x000d}, {0x01, 0x00ed, 0x000e}, {0x01, 0x00d8, 0x000f}, {0x01, 0x0038, 0x0010}, {0x01, 0x00ff, 0x0015}, {0x01, 0x0001, 0x0016}, {0x01, 0x0032, 0x0017}, {0x01, 0x0023, 0x0018}, {0x01, 0x00ce, 0x0019}, {0x01, 0x0023, 0x001a}, {0x01, 0x0032, 0x001b}, {0x01, 0x008d, 0x001c}, {0x01, 0x00ce, 0x001d}, {0x01, 0x008d, 0x001e}, {0x01, 0x0000, 0x001f}, {0x01, 0x0000, 0x0020}, {0x01, 0x00ff, 0x003e}, {0x01, 0x0003, 0x003f}, {0x01, 0x0000, 0x0040}, {0x01, 0x0035, 0x0041}, {0x01, 0x0053, 0x0042}, {0x01, 0x0069, 0x0043}, {0x01, 0x007c, 0x0044}, {0x01, 0x008c, 0x0045}, {0x01, 0x009a, 0x0046}, {0x01, 0x00a8, 0x0047}, {0x01, 0x00b4, 0x0048}, {0x01, 0x00bf, 0x0049}, {0x01, 0x00ca, 0x004a}, {0x01, 0x00d4, 0x004b}, {0x01, 0x00dd, 0x004c}, {0x01, 0x00e7, 0x004d}, {0x01, 0x00ef, 0x004e}, {0x01, 0x00f8, 0x004f}, {0x01, 0x00ff, 0x0050}, {0x01, 0x0001, 0x0056}, {0x01, 0x0060, 0x0057}, {0x01, 0x0040, 0x0058}, {0x01, 0x0011, 0x0059}, {0x01, 0x0001, 0x005a}, {0x02, 0x0007, 0x0005}, {0x02, 0xa048, 0x0000}, {0x02, 0x0007, 0x0005}, {0x02, 0x0015, 0x0006}, {0x02, 0x100a, 0x0007}, {0x02, 0xa048, 0x0000}, {0x02, 0xc002, 0x0001}, {0x02, 0x000f, 0x0005}, {0x02, 0xa048, 0x0000}, {0x05, 0x0022, 0x0004}, {0x05, 0x0025, 0x0001}, {0x05, 0x0000, 0x0000}, {0x05, 0x0026, 0x0001}, {0x05, 0x0001, 0x0000}, {0x05, 0x0027, 0x0001}, {0x05, 0x0000, 0x0000}, {0x05, 0x0001, 0x0001}, {0x05, 0x0000, 0x0000}, {0x05, 0x0021, 0x0001}, {0x05, 0x00d2, 0x0000}, {0x05, 0x0020, 0x0001}, {0x05, 0x0000, 0x0000}, {0x00, 0x0090, 0x0005}, {0x01, 0x00a6, 0x0000}, {0x02, 0x0007, 0x0005}, {0x02, 0x2000, 0x0000}, {0x05, 0x0022, 0x0004}, {0x05, 0x0015, 0x0001}, {0x05, 0x00ea, 0x0000}, {0x05, 0x0021, 0x0001}, {0x05, 0x00d2, 0x0000}, {0x05, 0x0023, 0x0001}, {0x05, 0x0003, 0x0000}, {0x05, 0x0030, 0x0001}, {0x05, 0x002b, 0x0000}, {0x05, 0x0031, 0x0001}, {0x05, 0x0023, 0x0000}, {0x05, 0x0032, 0x0001}, {0x05, 0x0023, 0x0000}, {0x05, 0x0033, 0x0001}, {0x05, 0x0023, 0x0000}, {0x05, 0x0034, 0x0001}, {0x05, 0x0002, 0x0000}, {0x05, 0x0050, 0x0001}, {0x05, 0x0000, 0x0000}, {0x05, 0x0051, 0x0001}, {0x05, 0x0000, 0x0000}, {0x05, 0x0052, 0x0001}, {0x05, 0x0000, 0x0000}, {0x05, 0x0054, 0x0001}, {0x05, 0x0001, 0x0000}, {0x00, 0x0000, 0x0001}, {0x00, 0x0000, 0x0002}, {0x00, 0x000c, 0x0003}, {0x00, 0x0000, 0x0004}, {0x00, 0x0090, 0x0005}, {0x00, 0x0000, 0x0006}, {0x00, 0x0040, 0x0007}, {0x00, 0x00c0, 0x0008}, {0x00, 0x004a, 0x0009}, {0x00, 0x0000, 0x000a}, {0x00, 0x0000, 0x000b}, {0x00, 0x0001, 0x000c}, {0x00, 0x0001, 0x000d}, {0x00, 0x0000, 0x000e}, {0x00, 0x0002, 0x000f}, {0x00, 0x0001, 0x0010}, {0x00, 0x0000, 0x0011}, {0x00, 0x0000, 0x0012}, {0x00, 0x0002, 0x0020}, {0x00, 0x0080, 0x0021}, {0x00, 0x0001, 0x0022}, {0x00, 0x00e0, 0x0023}, {0x00, 0x0000, 0x0024}, {0x00, 0x00d5, 0x0025}, {0x00, 0x0000, 0x0026}, {0x00, 0x000b, 0x0027}, {0x00, 0x0000, 0x0046}, {0x00, 0x0000, 0x0047}, {0x00, 0x0000, 0x0048}, {0x00, 0x0000, 0x0049}, {0x00, 0x0008, 0x004a}, {0xff, 0x0000, 0x00d0}, {0xff, 0x00d8, 0x00d1}, {0xff, 0x0000, 0x00d4}, {0xff, 0x0000, 0x00d5}, {0x01, 0x00a6, 0x0000}, {0x01, 0x0028, 0x0001}, {0x01, 0x0000, 0x0002}, {0x01, 0x000a, 0x0003}, {0x01, 0x0040, 0x0004}, {0x01, 0x0066, 0x0007}, {0x01, 0x0011, 0x0008}, {0x01, 0x0032, 0x0009}, {0x01, 0x00fd, 0x000a}, {0x01, 0x0038, 0x000b}, {0x01, 0x00d1, 0x000c}, {0x01, 0x00f7, 0x000d}, {0x01, 0x00ed, 0x000e}, {0x01, 0x00d8, 0x000f}, {0x01, 0x0038, 0x0010}, {0x01, 0x00ff, 0x0015}, {0x01, 0x0001, 0x0016}, {0x01, 0x0032, 0x0017}, {0x01, 0x0023, 0x0018}, {0x01, 0x00ce, 0x0019}, {0x01, 0x0023, 0x001a}, {0x01, 0x0032, 0x001b}, {0x01, 0x008d, 0x001c}, {0x01, 0x00ce, 0x001d}, {0x01, 0x008d, 0x001e}, {0x01, 0x0000, 0x001f}, {0x01, 0x0000, 0x0020}, {0x01, 0x00ff, 0x003e}, {0x01, 0x0003, 0x003f}, {0x01, 0x0000, 0x0040}, {0x01, 0x0035, 0x0041}, {0x01, 0x0053, 0x0042}, {0x01, 0x0069, 0x0043}, {0x01, 0x007c, 0x0044}, {0x01, 0x008c, 0x0045}, {0x01, 0x009a, 0x0046}, {0x01, 0x00a8, 0x0047}, {0x01, 0x00b4, 0x0048}, {0x01, 0x00bf, 0x0049}, {0x01, 0x00ca, 0x004a}, {0x01, 0x00d4, 0x004b}, {0x01, 0x00dd, 0x004c}, {0x01, 0x00e7, 0x004d}, {0x01, 0x00ef, 0x004e}, {0x01, 0x00f8, 0x004f}, {0x01, 0x00ff, 0x0050}, {0x01, 0x0001, 0x0056}, {0x01, 0x0060, 0x0057}, {0x01, 0x0040, 0x0058}, {0x01, 0x0011, 0x0059}, {0x01, 0x0001, 0x005a}, {0x02, 0x0007, 0x0005}, {0x02, 0xa048, 0x0000}, {0x02, 0x0007, 0x0005}, {0x02, 0x0015, 0x0006}, {0x02, 0x100a, 0x0007}, {0x02, 0xa048, 0x0000}, {0x02, 0xc002, 0x0001}, {0x02, 0x000f, 0x0005}, {0x02, 0xa048, 0x0000}, {0x05, 0x0022, 0x0004}, {0x05, 0x0025, 0x0001}, {0x05, 0x0000, 0x0000}, {0x05, 0x0026, 0x0001}, {0x05, 0x0001, 0x0000}, {0x05, 0x0027, 0x0001}, {0x05, 0x0000, 0x0000}, {0x05, 0x0001, 0x0001}, {0x05, 0x0000, 0x0000}, {0x05, 0x0021, 0x0001}, {0x05, 0x00d2, 0x0000}, {0x05, 0x0020, 0x0001}, {0x05, 0x0000, 0x0000}, {0x00, 0x0090, 0x0005}, {0x01, 0x00a6, 0x0000}, {0x01, 0x0003, 0x003f}, {0x01, 0x0001, 0x0056}, {0x01, 0x0011, 0x0008}, {0x01, 0x0032, 0x0009}, {0x01, 0xfffd, 0x000a}, {0x01, 0x0023, 0x000b}, {0x01, 0xffea, 0x000c}, {0x01, 0xfff4, 0x000d}, {0x01, 0xfffc, 0x000e}, {0x01, 0xffe3, 0x000f}, {0x01, 0x001f, 0x0010}, {0x01, 0x00a8, 0x0001}, {0x01, 0x0067, 0x0007}, {0x01, 0x0032, 0x0017}, {0x01, 0x0023, 0x0018}, {0x01, 0x00ce, 0x0019}, {0x01, 0x0023, 0x001a}, {0x01, 0x0032, 0x001b}, {0x01, 0x008d, 0x001c}, {0x01, 0x00ce, 0x001d}, {0x01, 0x008d, 0x001e}, {0x01, 0x00c8, 0x0015}, {0x01, 0x0032, 0x0016}, {0x01, 0x0000, 0x0011}, {0x01, 0x0000, 0x0012}, {0x01, 0x0000, 0x0013}, {0x01, 0x000a, 0x0003}, {0x02, 0xc002, 0x0001}, {0x02, 0x0007, 0x0005}, {0x02, 0xc000, 0x0001}, {0x02, 0x0000, 0x0005}, {0x02, 0x0007, 0x0005}, {0x02, 0x2000, 0x0000}, {0x05, 0x0022, 0x0004}, {0x05, 0x0015, 0x0001}, {0x05, 0x00ea, 0x0000}, {0x05, 0x0021, 0x0001}, {0x05, 0x00d2, 0x0000}, {0x05, 0x0023, 0x0001}, {0x05, 0x0003, 0x0000}, {0x05, 0x0030, 0x0001}, {0x05, 0x002b, 0x0000}, {0x05, 0x0031, 0x0001}, {0x05, 0x0023, 0x0000}, {0x05, 0x0032, 0x0001}, {0x05, 0x0023, 0x0000}, {0x05, 0x0033, 0x0001}, {0x05, 0x0023, 0x0000}, {0x05, 0x0034, 0x0001}, {0x05, 0x0002, 0x0000}, {0x05, 0x0050, 0x0001}, {0x05, 0x0000, 0x0000}, {0x05, 0x0051, 0x0001}, {0x05, 0x0000, 0x0000}, {0x05, 0x0052, 0x0001}, {0x05, 0x0000, 0x0000}, {0x05, 0x0054, 0x0001}, {0x05, 0x0001, 0x0000}, {0x00, 0x0000, 0x0001}, {0x00, 0x0000, 0x0002}, {0x00, 0x000c, 0x0003}, {0x00, 0x0000, 0x0004}, {0x00, 0x0090, 0x0005}, {0x00, 0x0000, 0x0006}, {0x00, 0x0040, 0x0007}, {0x00, 0x00c0, 0x0008}, {0x00, 0x004a, 0x0009}, {0x00, 0x0000, 0x000a}, {0x00, 0x0000, 0x000b}, {0x00, 0x0001, 0x000c}, {0x00, 0x0001, 0x000d}, {0x00, 0x0000, 0x000e}, {0x00, 0x0002, 0x000f}, {0x00, 0x0001, 0x0010}, {0x00, 0x0000, 0x0011}, {0x00, 0x0000, 0x0012}, {0x00, 0x0002, 0x0020}, {0x00, 0x0080, 0x0021}, {0x00, 0x0001, 0x0022}, {0x00, 0x00e0, 0x0023}, {0x00, 0x0000, 0x0024}, {0x00, 0x00d5, 0x0025}, {0x00, 0x0000, 0x0026}, {0x00, 0x000b, 0x0027}, {0x00, 0x0000, 0x0046}, {0x00, 0x0000, 0x0047}, {0x00, 0x0000, 0x0048}, {0x00, 0x0000, 0x0049}, {0x00, 0x0008, 0x004a}, {0xff, 0x0000, 0x00d0}, {0xff, 0x00d8, 0x00d1}, {0xff, 0x0000, 0x00d4}, {0xff, 0x0000, 0x00d5}, {0x01, 0x00a6, 0x0000}, {0x01, 0x0028, 0x0001}, {0x01, 0x0000, 0x0002}, {0x01, 0x000a, 0x0003}, {0x01, 0x0040, 0x0004}, {0x01, 0x0066, 0x0007}, {0x01, 0x0011, 0x0008}, {0x01, 0x0032, 0x0009}, {0x01, 0x00fd, 0x000a}, {0x01, 0x0038, 0x000b}, {0x01, 0x00d1, 0x000c}, {0x01, 0x00f7, 0x000d}, {0x01, 0x00ed, 0x000e}, {0x01, 0x00d8, 0x000f}, {0x01, 0x0038, 0x0010}, {0x01, 0x00ff, 0x0015}, {0x01, 0x0001, 0x0016}, {0x01, 0x0032, 0x0017}, {0x01, 0x0023, 0x0018}, {0x01, 0x00ce, 0x0019}, {0x01, 0x0023, 0x001a}, {0x01, 0x0032, 0x001b}, {0x01, 0x008d, 0x001c}, {0x01, 0x00ce, 0x001d}, {0x01, 0x008d, 0x001e}, {0x01, 0x0000, 0x001f}, {0x01, 0x0000, 0x0020}, {0x01, 0x00ff, 0x003e}, {0x01, 0x0003, 0x003f}, {0x01, 0x0000, 0x0040}, {0x01, 0x0035, 0x0041}, {0x01, 0x0053, 0x0042}, {0x01, 0x0069, 0x0043}, {0x01, 0x007c, 0x0044}, {0x01, 0x008c, 0x0045}, {0x01, 0x009a, 0x0046}, {0x01, 0x00a8, 0x0047}, {0x01, 0x00b4, 0x0048}, {0x01, 0x00bf, 0x0049}, {0x01, 0x00ca, 0x004a}, {0x01, 0x00d4, 0x004b}, {0x01, 0x00dd, 0x004c}, {0x01, 0x00e7, 0x004d}, {0x01, 0x00ef, 0x004e}, {0x01, 0x00f8, 0x004f}, {0x01, 0x00ff, 0x0050}, {0x01, 0x0001, 0x0056}, {0x01, 0x0060, 0x0057}, {0x01, 0x0040, 0x0058}, {0x01, 0x0011, 0x0059}, {0x01, 0x0001, 0x005a}, {0x02, 0x0007, 0x0005}, {0x02, 0xa048, 0x0000}, {0x02, 0x0007, 0x0005}, {0x02, 0x0015, 0x0006}, {0x02, 0x100a, 0x0007}, {0x02, 0xa048, 0x0000}, {0x02, 0xc002, 0x0001}, {0x02, 0x000f, 0x0005}, {0x02, 0xa048, 0x0000}, {0x05, 0x0022, 0x0004}, {0x05, 0x0025, 0x0001}, {0x05, 0x0000, 0x0000}, {0x05, 0x0026, 0x0001}, {0x05, 0x0001, 0x0000}, {0x05, 0x0027, 0x0001}, {0x05, 0x0000, 0x0000}, {0x05, 0x0001, 0x0001}, {0x05, 0x0000, 0x0000}, {0x05, 0x0021, 0x0001}, {0x05, 0x00d2, 0x0000}, {0x05, 0x0020, 0x0001}, {0x05, 0x0000, 0x0000}, {0x00, 0x0090, 0x0005}, {0x01, 0x00a6, 0x0000}, {0x02, 0x0007, 0x0005}, {0x02, 0x2000, 0x0000}, {0x05, 0x0022, 0x0004}, {0x05, 0x0015, 0x0001}, {0x05, 0x00ea, 0x0000}, {0x05, 0x0021, 0x0001}, {0x05, 0x00d2, 0x0000}, {0x05, 0x0023, 0x0001}, {0x05, 0x0003, 0x0000}, {0x05, 0x0030, 0x0001}, {0x05, 0x002b, 0x0000}, {0x05, 0x0031, 0x0001}, {0x05, 0x0023, 0x0000}, {0x05, 0x0032, 0x0001}, {0x05, 0x0023, 0x0000}, {0x05, 0x0033, 0x0001}, {0x05, 0x0023, 0x0000}, {0x05, 0x0034, 0x0001}, {0x05, 0x0002, 0x0000}, {0x05, 0x0050, 0x0001}, {0x05, 0x0000, 0x0000}, {0x05, 0x0051, 0x0001}, {0x05, 0x0000, 0x0000}, {0x05, 0x0052, 0x0001}, {0x05, 0x0000, 0x0000}, {0x05, 0x0054, 0x0001}, {0x05, 0x0001, 0x0000}, {0x00, 0x0000, 0x0001}, {0x00, 0x0000, 0x0002}, {0x00, 0x000c, 0x0003}, {0x00, 0x0000, 0x0004}, {0x00, 0x0090, 0x0005}, {0x00, 0x0000, 0x0006}, {0x00, 0x0040, 0x0007}, {0x00, 0x00c0, 0x0008}, {0x00, 0x004a, 0x0009}, {0x00, 0x0000, 0x000a}, {0x00, 0x0000, 0x000b}, {0x00, 0x0001, 0x000c}, {0x00, 0x0001, 0x000d}, {0x00, 0x0000, 0x000e}, {0x00, 0x0002, 0x000f}, {0x00, 0x0001, 0x0010}, {0x00, 0x0000, 0x0011}, {0x00, 0x0000, 0x0012}, {0x00, 0x0002, 0x0020}, {0x00, 0x0080, 0x0021}, {0x00, 0x0001, 0x0022}, {0x00, 0x00e0, 0x0023}, {0x00, 0x0000, 0x0024}, {0x00, 0x00d5, 0x0025}, {0x00, 0x0000, 0x0026}, {0x00, 0x000b, 0x0027}, {0x00, 0x0000, 0x0046}, {0x00, 0x0000, 0x0047}, {0x00, 0x0000, 0x0048}, {0x00, 0x0000, 0x0049}, {0x00, 0x0008, 0x004a}, {0xff, 0x0000, 0x00d0}, {0xff, 0x00d8, 0x00d1}, {0xff, 0x0000, 0x00d4}, {0xff, 0x0000, 0x00d5}, {0x01, 0x00a6, 0x0000}, {0x01, 0x0028, 0x0001}, {0x01, 0x0000, 0x0002}, {0x01, 0x000a, 0x0003}, {0x01, 0x0040, 0x0004}, {0x01, 0x0066, 0x0007}, {0x01, 0x0011, 0x0008}, {0x01, 0x0032, 0x0009}, {0x01, 0x00fd, 0x000a}, {0x01, 0x0038, 0x000b}, {0x01, 0x00d1, 0x000c}, {0x01, 0x00f7, 0x000d}, {0x01, 0x00ed, 0x000e}, {0x01, 0x00d8, 0x000f}, {0x01, 0x0038, 0x0010}, {0x01, 0x00ff, 0x0015}, {0x01, 0x0001, 0x0016}, {0x01, 0x0032, 0x0017}, {0x01, 0x0023, 0x0018}, {0x01, 0x00ce, 0x0019}, {0x01, 0x0023, 0x001a}, {0x01, 0x0032, 0x001b}, {0x01, 0x008d, 0x001c}, {0x01, 0x00ce, 0x001d}, {0x01, 0x008d, 0x001e}, {0x01, 0x0000, 0x001f}, {0x01, 0x0000, 0x0020}, {0x01, 0x00ff, 0x003e}, {0x01, 0x0003, 0x003f}, {0x01, 0x0000, 0x0040}, {0x01, 0x0035, 0x0041}, {0x01, 0x0053, 0x0042}, {0x01, 0x0069, 0x0043}, {0x01, 0x007c, 0x0044}, {0x01, 0x008c, 0x0045}, {0x01, 0x009a, 0x0046}, {0x01, 0x00a8, 0x0047}, {0x01, 0x00b4, 0x0048}, {0x01, 0x00bf, 0x0049}, {0x01, 0x00ca, 0x004a}, {0x01, 0x00d4, 0x004b}, {0x01, 0x00dd, 0x004c}, {0x01, 0x00e7, 0x004d}, {0x01, 0x00ef, 0x004e}, {0x01, 0x00f8, 0x004f}, {0x01, 0x00ff, 0x0050}, {0x01, 0x0001, 0x0056}, {0x01, 0x0060, 0x0057}, {0x01, 0x0040, 0x0058}, {0x01, 0x0011, 0x0059}, {0x01, 0x0001, 0x005a}, {0x02, 0x0007, 0x0005}, {0x02, 0xa048, 0x0000}, {0x02, 0x0007, 0x0005}, {0x02, 0x0015, 0x0006}, {0x02, 0x100a, 0x0007}, {0x02, 0xa048, 0x0000}, {0x02, 0xc002, 0x0001}, {0x02, 0x000f, 0x0005}, {0x02, 0xa048, 0x0000}, {0x05, 0x0022, 0x0004}, {0x05, 0x0025, 0x0001}, {0x05, 0x0000, 0x0000}, {0x05, 0x0026, 0x0001}, {0x05, 0x0001, 0x0000}, {0x05, 0x0027, 0x0001}, {0x05, 0x0000, 0x0000}, {0x05, 0x0001, 0x0001}, {0x05, 0x0000, 0x0000}, {0x05, 0x0021, 0x0001}, {0x05, 0x00d2, 0x0000}, {0x05, 0x0020, 0x0001}, {0x05, 0x0000, 0x0000}, {0x00, 0x0090, 0x0005}, {0x01, 0x00a6, 0x0000}, {0x05, 0x0026, 0x0001}, {0x05, 0x0001, 0x0000}, {0x05, 0x0027, 0x0001}, {0x05, 0x000f, 0x0000}, {0x01, 0x0003, 0x003f}, {0x01, 0x0001, 0x0056}, {0x01, 0x0011, 0x0008}, {0x01, 0x0032, 0x0009}, {0x01, 0xfffd, 0x000a}, {0x01, 0x0023, 0x000b}, {0x01, 0xffea, 0x000c}, {0x01, 0xfff4, 0x000d}, {0x01, 0xfffc, 0x000e}, {0x01, 0xffe3, 0x000f}, {0x01, 0x001f, 0x0010}, {0x01, 0x00a8, 0x0001}, {0x01, 0x0067, 0x0007}, {0x01, 0x0042, 0x0051}, {0x01, 0x0051, 0x0053}, {0x01, 0x000a, 0x0003}, {0x02, 0xc002, 0x0001}, {0x02, 0x0007, 0x0005}, {0x02, 0xc000, 0x0001}, {0x02, 0x0000, 0x0005}, {0x02, 0x0007, 0x0005}, {0x02, 0x2000, 0x0000}, {0x05, 0x0022, 0x0004}, {0x05, 0x0015, 0x0001}, {0x05, 0x00ea, 0x0000}, {0x05, 0x0021, 0x0001}, {0x05, 0x00d2, 0x0000}, {0x05, 0x0023, 0x0001}, {0x05, 0x0003, 0x0000}, {0x05, 0x0030, 0x0001}, {0x05, 0x002b, 0x0000}, {0x05, 0x0031, 0x0001}, {0x05, 0x0023, 0x0000}, {0x05, 0x0032, 0x0001}, {0x05, 0x0023, 0x0000}, {0x05, 0x0033, 0x0001}, {0x05, 0x0023, 0x0000}, {0x05, 0x0034, 0x0001}, {0x05, 0x0002, 0x0000}, {0x05, 0x0050, 0x0001}, {0x05, 0x0000, 0x0000}, {0x05, 0x0051, 0x0001}, {0x05, 0x0000, 0x0000}, {0x05, 0x0052, 0x0001}, {0x05, 0x0000, 0x0000}, {0x05, 0x0054, 0x0001}, {0x05, 0x0001, 0x0000}, {0x00, 0x0000, 0x0001}, {0x00, 0x0000, 0x0002}, {0x00, 0x000c, 0x0003}, {0x00, 0x0000, 0x0004}, {0x00, 0x0090, 0x0005}, {0x00, 0x0000, 0x0006}, {0x00, 0x0040, 0x0007}, {0x00, 0x00c0, 0x0008}, {0x00, 0x004a, 0x0009}, {0x00, 0x0000, 0x000a}, {0x00, 0x0000, 0x000b}, {0x00, 0x0001, 0x000c}, {0x00, 0x0001, 0x000d}, {0x00, 0x0000, 0x000e}, {0x00, 0x0002, 0x000f}, {0x00, 0x0001, 0x0010}, {0x00, 0x0000, 0x0011}, {0x00, 0x0000, 0x0012}, {0x00, 0x0002, 0x0020}, {0x00, 0x0080, 0x0021}, {0x00, 0x0001, 0x0022}, {0x00, 0x00e0, 0x0023}, {0x00, 0x0000, 0x0024}, {0x00, 0x00d5, 0x0025}, {0x00, 0x0000, 0x0026}, {0x00, 0x000b, 0x0027}, {0x00, 0x0000, 0x0046}, {0x00, 0x0000, 0x0047}, {0x00, 0x0000, 0x0048}, {0x00, 0x0000, 0x0049}, {0x00, 0x0008, 0x004a}, {0xff, 0x0000, 0x00d0}, {0xff, 0x00d8, 0x00d1}, {0xff, 0x0000, 0x00d4}, {0xff, 0x0000, 0x00d5}, {0x01, 0x00a6, 0x0000}, {0x01, 0x0028, 0x0001}, {0x01, 0x0000, 0x0002}, {0x01, 0x000a, 0x0003}, {0x01, 0x0040, 0x0004}, {0x01, 0x0066, 0x0007}, {0x01, 0x0011, 0x0008}, {0x01, 0x0032, 0x0009}, {0x01, 0x00fd, 0x000a}, {0x01, 0x0038, 0x000b}, {0x01, 0x00d1, 0x000c}, {0x01, 0x00f7, 0x000d}, {0x01, 0x00ed, 0x000e}, {0x01, 0x00d8, 0x000f}, {0x01, 0x0038, 0x0010}, {0x01, 0x00ff, 0x0015}, {0x01, 0x0001, 0x0016}, {0x01, 0x0032, 0x0017}, {0x01, 0x0023, 0x0018}, {0x01, 0x00ce, 0x0019}, {0x01, 0x0023, 0x001a}, {0x01, 0x0032, 0x001b}, {0x01, 0x008d, 0x001c}, {0x01, 0x00ce, 0x001d}, {0x01, 0x008d, 0x001e}, {0x01, 0x0000, 0x001f}, {0x01, 0x0000, 0x0020}, {0x01, 0x00ff, 0x003e}, {0x01, 0x0003, 0x003f}, {0x01, 0x0000, 0x0040}, {0x01, 0x0035, 0x0041}, {0x01, 0x0053, 0x0042}, {0x01, 0x0069, 0x0043}, {0x01, 0x007c, 0x0044}, {0x01, 0x008c, 0x0045}, {0x01, 0x009a, 0x0046}, {0x01, 0x00a8, 0x0047}, {0x01, 0x00b4, 0x0048}, {0x01, 0x00bf, 0x0049}, {0x01, 0x00ca, 0x004a}, {0x01, 0x00d4, 0x004b}, {0x01, 0x00dd, 0x004c}, {0x01, 0x00e7, 0x004d}, {0x01, 0x00ef, 0x004e}, {0x01, 0x00f8, 0x004f}, {0x01, 0x00ff, 0x0050}, {0x01, 0x0001, 0x0056}, {0x01, 0x0060, 0x0057}, {0x01, 0x0040, 0x0058}, {0x01, 0x0011, 0x0059}, {0x01, 0x0001, 0x005a}, {0x02, 0x0007, 0x0005}, {0x02, 0xa048, 0x0000}, {0x02, 0x0007, 0x0005}, {0x02, 0x0015, 0x0006}, {0x02, 0x100a, 0x0007}, {0x02, 0xa048, 0x0000}, {0x02, 0xc002, 0x0001}, {0x02, 0x000f, 0x0005}, {0x02, 0xa048, 0x0000}, {0x05, 0x0022, 0x0004}, {0x05, 0x0025, 0x0001}, {0x05, 0x0000, 0x0000}, {0x05, 0x0026, 0x0001}, {0x05, 0x0001, 0x0000}, {0x05, 0x0027, 0x0001}, {0x05, 0x0000, 0x0000}, {0x05, 0x0001, 0x0001}, {0x05, 0x0000, 0x0000}, {0x05, 0x0021, 0x0001}, {0x05, 0x00d2, 0x0000}, {0x05, 0x0020, 0x0001}, {0x05, 0x0000, 0x0000}, {0x00, 0x0090, 0x0005}, {0x01, 0x00a6, 0x0000}, {0x02, 0x0007, 0x0005}, {0x02, 0x2000, 0x0000}, {0x05, 0x0022, 0x0004}, {0x05, 0x0015, 0x0001}, {0x05, 0x00ea, 0x0000}, {0x05, 0x0021, 0x0001}, {0x05, 0x00d2, 0x0000}, {0x05, 0x0023, 0x0001}, {0x05, 0x0003, 0x0000}, {0x05, 0x0030, 0x0001}, {0x05, 0x002b, 0x0000}, {0x05, 0x0031, 0x0001}, {0x05, 0x0023, 0x0000}, {0x05, 0x0032, 0x0001}, {0x05, 0x0023, 0x0000}, {0x05, 0x0033, 0x0001}, {0x05, 0x0023, 0x0000}, {0x05, 0x0034, 0x0001}, {0x05, 0x0002, 0x0000}, {0x05, 0x0050, 0x0001}, {0x05, 0x0000, 0x0000}, {0x05, 0x0051, 0x0001}, {0x05, 0x0000, 0x0000}, {0x05, 0x0052, 0x0001}, {0x05, 0x0000, 0x0000}, {0x05, 0x0054, 0x0001}, {0x05, 0x0001, 0x0000}, {0x00, 0x0000, 0x0001}, {0x00, 0x0000, 0x0002}, {0x00, 0x000c, 0x0003}, {0x00, 0x0000, 0x0004}, {0x00, 0x0090, 0x0005}, {0x00, 0x0000, 0x0006}, {0x00, 0x0040, 0x0007}, {0x00, 0x00c0, 0x0008}, {0x00, 0x004a, 0x0009}, {0x00, 0x0000, 0x000a}, {0x00, 0x0000, 0x000b}, {0x00, 0x0001, 0x000c}, {0x00, 0x0001, 0x000d}, {0x00, 0x0000, 0x000e}, {0x00, 0x0002, 0x000f}, {0x00, 0x0001, 0x0010}, {0x00, 0x0000, 0x0011}, {0x00, 0x0000, 0x0012}, {0x00, 0x0002, 0x0020}, {0x00, 0x0080, 0x0021}, {0x00, 0x0001, 0x0022}, {0x00, 0x00e0, 0x0023}, {0x00, 0x0000, 0x0024}, {0x00, 0x00d5, 0x0025}, {0x00, 0x0000, 0x0026}, {0x00, 0x000b, 0x0027}, {0x00, 0x0000, 0x0046}, {0x00, 0x0000, 0x0047}, {0x00, 0x0000, 0x0048}, {0x00, 0x0000, 0x0049}, {0x00, 0x0008, 0x004a}, {0xff, 0x0000, 0x00d0}, {0xff, 0x00d8, 0x00d1}, {0xff, 0x0000, 0x00d4}, {0xff, 0x0000, 0x00d5}, {0x01, 0x00a6, 0x0000}, {0x01, 0x0028, 0x0001}, {0x01, 0x0000, 0x0002}, {0x01, 0x000a, 0x0003}, {0x01, 0x0040, 0x0004}, {0x01, 0x0066, 0x0007}, {0x01, 0x0011, 0x0008}, {0x01, 0x0032, 0x0009}, {0x01, 0x00fd, 0x000a}, {0x01, 0x0038, 0x000b}, {0x01, 0x00d1, 0x000c}, {0x01, 0x00f7, 0x000d}, {0x01, 0x00ed, 0x000e}, {0x01, 0x00d8, 0x000f}, {0x01, 0x0038, 0x0010}, {0x01, 0x00ff, 0x0015}, {0x01, 0x0001, 0x0016}, {0x01, 0x0032, 0x0017}, {0x01, 0x0023, 0x0018}, {0x01, 0x00ce, 0x0019}, {0x01, 0x0023, 0x001a}, {0x01, 0x0032, 0x001b}, {0x01, 0x008d, 0x001c}, {0x01, 0x00ce, 0x001d}, {0x01, 0x008d, 0x001e}, {0x01, 0x0000, 0x001f}, {0x01, 0x0000, 0x0020}, {0x01, 0x00ff, 0x003e}, {0x01, 0x0003, 0x003f}, {0x01, 0x0000, 0x0040}, {0x01, 0x0035, 0x0041}, {0x01, 0x0053, 0x0042}, {0x01, 0x0069, 0x0043}, {0x01, 0x007c, 0x0044}, {0x01, 0x008c, 0x0045}, {0x01, 0x009a, 0x0046}, {0x01, 0x00a8, 0x0047}, {0x01, 0x00b4, 0x0048}, {0x01, 0x00bf, 0x0049}, {0x01, 0x00ca, 0x004a}, {0x01, 0x00d4, 0x004b}, {0x01, 0x00dd, 0x004c}, {0x01, 0x00e7, 0x004d}, {0x01, 0x00ef, 0x004e}, {0x01, 0x00f8, 0x004f}, {0x01, 0x00ff, 0x0050}, {0x01, 0x0001, 0x0056}, {0x01, 0x0060, 0x0057}, {0x01, 0x0040, 0x0058}, {0x01, 0x0011, 0x0059}, {0x01, 0x0001, 0x005a}, {0x02, 0x0007, 0x0005}, {0x02, 0xa048, 0x0000}, {0x02, 0x0007, 0x0005}, {0x02, 0x0015, 0x0006}, {0x02, 0x100a, 0x0007}, {0x02, 0xa048, 0x0000}, {0x02, 0xc002, 0x0001}, {0x02, 0x000f, 0x0005}, {0x02, 0xa048, 0x0000}, {0x05, 0x0022, 0x0004}, {0x05, 0x0025, 0x0001}, {0x05, 0x0000, 0x0000}, {0x05, 0x0026, 0x0001}, {0x05, 0x0001, 0x0000}, {0x05, 0x0027, 0x0001}, {0x05, 0x0000, 0x0000}, {0x05, 0x0001, 0x0001}, {0x05, 0x0000, 0x0000}, {0x05, 0x0021, 0x0001}, {0x05, 0x00d2, 0x0000}, {0x05, 0x0020, 0x0001}, {0x05, 0x0000, 0x0000}, {0x00, 0x0090, 0x0005}, {0x01, 0x00a6, 0x0000}, {0x05, 0x0026, 0x0001}, {0x05, 0x0001, 0x0000}, {0x05, 0x0027, 0x0001}, {0x05, 0x001e, 0x0000}, {0x01, 0x0003, 0x003f}, {0x01, 0x0001, 0x0056}, {0x01, 0x0011, 0x0008}, {0x01, 0x0032, 0x0009}, {0x01, 0xfffd, 0x000a}, {0x01, 0x0023, 0x000b}, {0x01, 0xffea, 0x000c}, {0x01, 0xfff4, 0x000d}, {0x01, 0xfffc, 0x000e}, {0x01, 0xffe3, 0x000f}, {0x01, 0x001f, 0x0010}, {0x01, 0x00a8, 0x0001}, {0x01, 0x0067, 0x0007}, {0x01, 0x0042, 0x0051}, {0x01, 0x0051, 0x0053}, {0x01, 0x000a, 0x0003}, {0x02, 0xc002, 0x0001}, {0x02, 0x0007, 0x0005}, {0x01, 0x0042, 0x0051}, {0x01, 0x0051, 0x0053}, {0x05, 0x0026, 0x0001}, {0x05, 0x0001, 0x0000}, {0x05, 0x0027, 0x0001}, {0x05, 0x002d, 0x0000}, {0x01, 0x0003, 0x003f}, {0x01, 0x0001, 0x0056}, {0x02, 0xc000, 0x0001}, {0x02, 0x0000, 0x0005}, {0x00, 0x0000, 0x0000} }; /* Unknow camera from Ori Usbid 0x0000:0x0000 */ static __u16 spca501c_mysterious_open_data[][3] = { /* Based on snoops from Ori Cohen */ {0x02, 0x000f, 0x0005}, {0x02, 0xa048, 0x0000}, {0x05, 0x0022, 0x0004}, /* DSP Registers */ {0x01, 0x0016, 0x0011}, //RGB offset {0x01, 0x0000, 0x0012}, {0x01, 0x0006, 0x0013}, {0x01, 0x0078, 0x0051}, {0x01, 0x0040, 0x0052}, {0x01, 0x0046, 0x0053}, {0x01, 0x0040, 0x0054}, {0x00, 0x0025, 0x0000}, //{0x00, 0x0000, 0x0000 }, /* Part 2 */ /* TG Registers */ {0x00, 0x0026, 0x0000}, {0x00, 0x0001, 0x0000}, {0x00, 0x0027, 0x0000}, {0x00, 0x008a, 0x0000}, {0x02, 0x0007, 0x0005}, {0x02, 0x2000, 0x0000}, {0x05, 0x0022, 0x0004}, {0x05, 0x0015, 0x0001}, {0x05, 0x00ea, 0x0000}, {0x05, 0x0021, 0x0001}, {0x05, 0x00d2, 0x0000}, {0x05, 0x0023, 0x0001}, {0x05, 0x0003, 0x0000}, {0x05, 0x0030, 0x0001}, {0x05, 0x002b, 0x0000}, {0x05, 0x0031, 0x0001}, {0x05, 0x0023, 0x0000}, {0x05, 0x0032, 0x0001}, {0x05, 0x0023, 0x0000}, {0x05, 0x0033, 0x0001}, {0x05, 0x0023, 0x0000}, {0x05, 0x0034, 0x0001}, {0x05, 0x0002, 0x0000}, {0x05, 0x0050, 0x0001}, {0x05, 0x0000, 0x0000}, {0x05, 0x0051, 0x0001}, {0x05, 0x0000, 0x0000}, {0x05, 0x0052, 0x0001}, {0x05, 0x0000, 0x0000}, {0x05, 0x0054, 0x0001}, {0x05, 0x0001, 0x0000}, {0, 0, 0} }; static __u16 spca501c_mysterious_init_data[][3] = { /* Based on snoops from Ori Cohen */ /* Part 3 */ /* TG registers */ //{0x00, 0x0000, 0x0000 }, {0x00, 0x0000, 0x0001}, {0x00, 0x0000, 0x0002}, {0x00, 0x0006, 0x0003}, {0x00, 0x0000, 0x0004}, {0x00, 0x0090, 0x0005}, {0x00, 0x0000, 0x0006}, {0x00, 0x0040, 0x0007}, {0x00, 0x00c0, 0x0008}, {0x00, 0x004a, 0x0009}, {0x00, 0x0000, 0x000a}, {0x00, 0x0000, 0x000b}, {0x00, 0x0001, 0x000c}, {0x00, 0x0001, 0x000d}, {0x00, 0x0000, 0x000e}, {0x00, 0x0002, 0x000f}, {0x00, 0x0001, 0x0010}, {0x00, 0x0000, 0x0011}, {0x00, 0x0001, 0x0012}, {0x00, 0x0002, 0x0020}, {0x00, 0x0080, 0x0021}, //640 {0x00, 0x0001, 0x0022}, {0x00, 0x00e0, 0x0023}, //480 {0x00, 0x0000, 0x0024}, // Offset H hight {0x00, 0x00d3, 0x0025}, // low {0x00, 0x0000, 0x0026}, // Offset V {0x00, 0x000d, 0x0027}, // low {0x00, 0x0000, 0x0046}, {0x00, 0x0000, 0x0047}, {0x00, 0x0000, 0x0048}, {0x00, 0x0000, 0x0049}, {0x00, 0x0008, 0x004a}, /* DSP Registers */ {0x01, 0x00a6, 0x0000}, {0x01, 0x0028, 0x0001}, {0x01, 0x0000, 0x0002}, {0x01, 0x000a, 0x0003}, // Level Calc bit7 ->1 Auto {0x01, 0x0040, 0x0004}, {0x01, 0x0066, 0x0007}, {0x01, 0x000f, 0x0008}, // A11 Color correction coeff {0x01, 0x002d, 0x0009}, // A12 {0x01, 0x0005, 0x000a}, // A13 {0x01, 0x0023, 0x000b}, // A21 {0x01, 0x00e0, 0x000c}, // A22 {0x01, 0x00fd, 0x000d}, // A23 {0x01, 0x00f4, 0x000e}, // A31 {0x01, 0x00e4, 0x000f}, // A32 {0x01, 0x0028, 0x0010}, // A33 {0x01, 0x00ff, 0x0015}, // Reserved {0x01, 0x0001, 0x0016}, // Reserved {0x01, 0x0032, 0x0017}, // Win1 Start begin {0x01, 0x0023, 0x0018}, {0x01, 0x00ce, 0x0019}, {0x01, 0x0023, 0x001a}, {0x01, 0x0032, 0x001b}, {0x01, 0x008d, 0x001c}, {0x01, 0x00ce, 0x001d}, {0x01, 0x008d, 0x001e}, {0x01, 0x0000, 0x001f}, {0x01, 0x0000, 0x0020}, //Win1 Start end {0x01, 0x00ff, 0x003e}, //Reserved begin {0x01, 0x0002, 0x003f}, {0x01, 0x0000, 0x0040}, {0x01, 0x0035, 0x0041}, {0x01, 0x0053, 0x0042}, {0x01, 0x0069, 0x0043}, {0x01, 0x007c, 0x0044}, {0x01, 0x008c, 0x0045}, {0x01, 0x009a, 0x0046}, {0x01, 0x00a8, 0x0047}, {0x01, 0x00b4, 0x0048}, {0x01, 0x00bf, 0x0049}, {0x01, 0x00ca, 0x004a}, {0x01, 0x00d4, 0x004b}, {0x01, 0x00dd, 0x004c}, {0x01, 0x00e7, 0x004d}, {0x01, 0x00ef, 0x004e}, {0x01, 0x00f8, 0x004f}, {0x01, 0x00ff, 0x0050}, {0x01, 0x0003, 0x0056}, // Reserved end {0x01, 0x0060, 0x0057}, //Edge Gain {0x01, 0x0040, 0x0058}, // {0x01, 0x0011, 0x0059}, //Edge Bandwidth {0x01, 0x0001, 0x005a}, // {0x02, 0x0007, 0x0005}, {0x02, 0xa048, 0x0000}, {0x02, 0x0007, 0x0005}, {0x02, 0x0015, 0x0006}, {0x02, 0x200a, 0x0007}, {0x02, 0xa048, 0x0000}, {0x02, 0xc000, 0x0001}, {0x02, 0x000f, 0x0005}, {0x02, 0xa048, 0x0000}, {0x05, 0x0022, 0x0004}, {0x05, 0x0025, 0x0001}, {0x05, 0x0000, 0x0000}, /* Part 4 */ {0x05, 0x0026, 0x0001}, {0x05, 0x0001, 0x0000}, {0x05, 0x0027, 0x0001}, {0x05, 0x0000, 0x0000}, {0x05, 0x0001, 0x0001}, {0x05, 0x0000, 0x0000}, {0x05, 0x0021, 0x0001}, {0x05, 0x00d2, 0x0000}, {0x05, 0x0020, 0x0001}, {0x05, 0x0000, 0x0000}, {0x00, 0x0090, 0x0005}, {0x01, 0x00a6, 0x0000}, {0x02, 0x0000, 0x0005}, {0x05, 0x0026, 0x0001}, {0x05, 0x0001, 0x0000}, {0x05, 0x0027, 0x0001}, {0x05, 0x004e, 0x0000}, /* Part 5 */ {0x01, 0x0003, 0x003f}, {0x01, 0x0001, 0x0056}, {0x01, 0x000f, 0x0008}, {0x01, 0x002d, 0x0009}, {0x01, 0x0005, 0x000a}, {0x01, 0x0023, 0x000b}, {0x01, 0xffe0, 0x000c}, {0x01, 0xfffd, 0x000d}, {0x01, 0xfff4, 0x000e}, {0x01, 0xffe4, 0x000f}, {0x01, 0x0028, 0x0010}, {0x01, 0x00a8, 0x0001}, {0x01, 0x0066, 0x0007}, {0x01, 0x0032, 0x0017}, {0x01, 0x0023, 0x0018}, {0x01, 0x00ce, 0x0019}, {0x01, 0x0023, 0x001a}, {0x01, 0x0032, 0x001b}, {0x01, 0x008d, 0x001c}, {0x01, 0x00ce, 0x001d}, {0x01, 0x008d, 0x001e}, {0x01, 0x00c8, 0x0015}, //c8 Poids fort Luma {0x01, 0x0032, 0x0016}, //32 {0x01, 0x0016, 0x0011}, //R 00 {0x01, 0x0016, 0x0012}, //G 00 {0x01, 0x0016, 0x0013}, //B 00 {0x01, 0x000a, 0x0003}, {0x02, 0xc002, 0x0001}, {0x02, 0x0007, 0x0005}, {0, 0, 0} }; /**************** Old code from spca50x *******************************/ /********************************************************************** * Get average luminance **********************************************************************/ static inline __u8 get_avg_lum(struct usb_spca50x *spca50x) { __u8 luminance; //The average luminance from camera switch (PWC_SC(spca50x)->pwc_info.bridge) { case BRIDGE_SPCA501: luminance = spca50x_reg_read(spca50x->dev, SPCA501_REG_CCDSP, 0x26, 2) >> 8; break; #ifdef SPCA50X_ENABLE_EXP_BRIGHTNESS case BRIDGE_SPCA561: case BRIDGE_SPCA508: luminance = spca50x_reg_read(spca50x->dev, 0, 0x8621, 1); break; #endif /* SPCA50X_ENABLE_EXP_BRIGHTNESS */ default: luminance = 0; break; } return luminance; } /********************************************************************** * Get average R-G and B-G **********************************************************************/ static inline __u8 get_avg_RG(struct usb_spca50x *spca50x) { __u8 rg; //The average R-G for window5 switch (PWC_SC(spca50x)->pwc_info.bridge) { case BRIDGE_SPCA501: rg = spca50x_reg_read(spca50x->dev, SPCA501_REG_CCDSP, 0x30, 2) >> 8; break; default: rg = 0; break; } return rg; } /********************************************************************** * Get average B-G **********************************************************************/ static inline __u8 get_avg_BG(struct usb_spca50x *spca50x) { __u8 bg; //The average B-G for window5 switch (PWC_SC(spca50x)->pwc_info.bridge) { case BRIDGE_SPCA501: bg = spca50x_reg_read(spca50x->dev, SPCA501_REG_CCDSP, 0x2f, 2) >> 8; break; default: bg = 0; break; } return bg; } /********************************************************************** * spca50x_get_whiteness * Function reads the "whiteness parameter" from the camera * Actually "whiteness" is a parameter that close to brighness * but currently ununderstanded * Receives the pointer to the description structure * returns the value of whiteness **********************************************************************/ static inline __u16 spca50x_get_whiteness(struct usb_spca50x *spca50x) { #ifdef SPCA50X_ENABLE_EXPERIMENTAL __u16 whiteness = 0; //the "whiteness" parameter __u16 br = 0; //the brightness value int i; // just an index variable __u16 mask; //the bit mask to set bit in p.contrast __u8 br_table[] = { 128, 64, 32, 16, 0 }; //the brightness table //this parameter is known for spca501 only if (PWC_SC(spca50x)->pwc_info.bridge != BRIDGE_SPCA501) return 0; /* Getting "whiteness" (strange parameters in TG 0x0D */ br = spca50x_reg_read(spca50x->dev, 0x0, 0x0d, 2); for (i = 0, mask = 1; br_table[i]; i++, mask <<= 1) { if (br & br_table[i]) whiteness |= mask; } whiteness <<= 4; return whiteness; #else /* SPCA50X_ENABLE_EXPERIMENTAL */ return 0; #endif /* SPCA50X_ENABLE_EXPERIMENTAL */ } /********************************************************************** * spca50x_set_whiteness * Function sets the "whiteness parameter" from the camera * Actually "whiteness" is a parameter that close to brighness * but currently ununderstanded * Receives the pointer to the description structure * and whiteness value **********************************************************************/ static inline void spca50x_set_whiteness(struct usb_spca50x *spca50x, __u8 whiteness) { #ifdef SPCA50X_ENABLE_EXPERIMENTAL __u8 br_high = 0; //the contrast value __u8 br_out = 0; // contrast output value int i; // just an index variable __u16 mask; //the bit mask to set bit in contrast __u8 br_table[] = { 128, 64, 32, 16, 0 }; //the brightness table //this parameter is known for spca501 only if (spca50x->bridge != BRIDGE_SPCA501) return; PDEBUG(3, "Setting whiteness %d", whiteness); br_high = whiteness >> 4; for (i = 0, mask = 1; br_table[i]; i++, mask <<= 1) { if (br_high & mask) br_out |= br_table[i]; } PDEBUG(3, "Writing whiteness %d", br_out); spca50x_reg_write(spca50x->dev, 0x0, 0x0d, br_out); #else /* SPCA50X_ENABLE_EXPERIMENTAL */ return; #endif /* SPCA50X_ENABLE_EXPERIMENTAL */ } /********************************************************************** * autobrightness * Function tries to control brightness * Receives the pointer to camera descriptor and * target average luminance ***********************************************************************/ static inline void autobrightness(struct usb_spca50x *spca50x) { __u8 cur_lum = 0; //The current luminance __u8 brightness = 0; char br_set = 0; //If the brightness should be set __u8 lum = spca50x->lum_level; //The desired luminance level __u8 delta = lum >> 3; //overshooting that we shouldn't care #ifdef SPCA50X_ENABLE_EXPERIMENTAL __u8 whiteness = 0; //whiteness level char wh_set = 0; //If whiteness must be set #endif /* SPCA50X_ENABLE_EXPERIMENTAL */ if (in_interrupt()) { PDEBUG(2, "In intr"); return; } //// /* * This algorithm may be dangerous. We MUST be sure that the * !spca50x->streaming will be set to 0 BEFORE kfree(spca50x) */ //// if (!spca50x->streaming) //No isoc return; cur_lum = get_avg_lum(spca50x); brightness = spca50x->brightness >> 8; if (cur_lum < (lum - delta)) { if (brightness < 255) { brightness++; br_set = 1; } #ifdef SPCA50X_ENABLE_EXPERIMENTAL else { whiteness = spca50x->whiteness >> 12; if (whiteness < 15) { whiteness++; wh_set = 1; } } #endif /* SPCA50X_ENABLE_EXPERIMENTAL */ } if (cur_lum > (lum + delta) && brightness > 0) { brightness--; br_set = 1; } #ifdef SPCA50X_ENABLE_EXPERIMENTAL if ((spca50x->nstable == NSTABLE_MAX || spca50x->nunstable == NUNSTABLE_MAX) && brightness < MIN_BRIGHTNESS) { whiteness = spca50x->whiteness >> 12; if (whiteness > 0) { whiteness--; wh_set = 1; } } if (wh_set) { spca50x_set_whiteness(spca50x, whiteness << 4); spca50x->whiteness = whiteness << 12; spca50x->nstable = 0; spca50x->nunstable = 0; return; //if we have set whiteness, we needn't care about brightness } #endif /* SPCA50X_ENABLE_EXPERIMENTAL */ if (br_set) { // spca50x_set_brightness (spca50x, brightness); spca50x->brightness = brightness << 8; #ifdef SPCA50X_ENABLE_EXPERIMENTAL if (spca50x->nunstable < NUNSTABLE_MAX) spca50x->nunstable++; spca50x->nstable = 0; #endif /* SPCA50X_ENABLE_EXPERIMENTAL */ } #ifdef SPCA50X_ENABLE_EXPERIMENTAL else { if (spca50x->nstable < NSTABLE_MAX) spca50x->nstable++; else spca50x->nunstable = 0; } #endif /* SPCA50X_ENABLE_EXPERIMENTAL */ } #endif /* SPCA501_INIT_H */ //eof pwcbsd/spca5xx-20060402/drivers/usb/spca505_init.h000744 000423 000000 00000034032 10546676142 021725 0ustar00luigiwheel000000 000000 /* * SPCA505 chip based cameras initialization data * */ #ifndef SPCA505_INIT_H #define SPCA505_INIT_H /* * Data to initialize a SPCA505. Common to the CCD and external modes */ static __u16 spca505_init_data[][3] = { /* line bmRequest,value,index */ /* 1819 */ {SPCA50X_REG_GLOBAL, SPCA50X_GMISC3_SAA7113RST, SPCA50X_GLOBAL_MISC3}, /* Sensor reset */ /* 1822 */ {SPCA50X_REG_GLOBAL, 0x0, SPCA50X_GLOBAL_MISC3}, /* 1825 */ {SPCA50X_REG_GLOBAL, 0x0, SPCA50X_GLOBAL_MISC1}, /* Block USB reset */ /* 1828 */ {SPCA50X_REG_GLOBAL, SPCA50X_GMISC0_IDSEL, SPCA50X_GLOBAL_MISC0}, /* 1831 */ {0x5, 0x1, 0x10}, /* Maybe power down some stuff */ /* 1834 */ {0x5, 0xf, 0x11}, /* Setup internal CCD ? */ /* 1837 */ {0x6, 0x10, 0x8}, /* 1840 */ {0x6, 0x0, 0x9}, /* 1843 */ {0x6, 0x0, 0x0a}, /* 1846 */ {0x6, 0x0, 0x0b}, /* 1849 */ {0x6, 0x10, 0xc}, /* 1852 */ {0x6, 0x0, 0xd}, /* 1855 */ {0x6, 0x0, 0xe}, /* 1858 */ {0x6, 0x0, 0xf}, /* 1861 */ {0x6, 0x10, 0x10}, /* 1864 */ {0x6, 0x2, 0x11}, /* 1867 */ {0x6, 0x0, 0x12}, /* 1870 */ {0x6, 0x4, 0x13}, /* 1873 */ {0x6, 0x2, 0x14}, /* 1876 */ {0x6, 0x8a, 0x51}, /* 1879 */ {0x6, 0x40, 0x52}, /* 1882 */ {0x6, 0xb6, 0x53}, /* 1885 */ {0x6, 0x3d, 0x54}, {0, 0, 0} }; /* * Data to initialize the camera using the internal CCD */ static __u16 spca505_open_data_ccd[][3] = { /* line bmRequest,value,index */ /* Internal CCD data set */ /* 1891 */ {0x3, 0x4, 0x1}, /* This could be a reset */ /* 1894 */ {0x3, 0x0, 0x1}, /* Setup compression and image registers. 0x6 and 0x7 seem to be related to H&V hold, and are resolution mode specific */ /* 1897 */ {0x4, 0x10, 0x1}, /* DIFF(0x50), was (0x10) */ /* 1900 */ {0x4, 0x0, 0x4}, /* 1903 */ {0x4, 0x0, 0x5}, /* 1906 */ {0x4, 0x20, 0x6}, /* 1909 */ {0x4, 0x20, 0x7}, /* 1912 */ {0x8, 0xa, 0x0}, /* DIFF (0x4a), was (0xa) */ /* 1915 */ {0x5, 0x0, 0x10}, /* 1918 */ {0x5, 0x0, 0x11}, /* 1921 */ {0x5, 0x0, 0x0}, /* DIFF not written */ /* 1924 */ {0x5, 0x0, 0x1}, /* DIFF not written */ /* 1927 */ {0x5, 0x0, 0x2}, /* DIFF not written */ /* 1930 */ {0x5, 0x0, 0x3}, /* DIFF not written */ /* 1933 */ {0x5, 0x0, 0x4}, /* DIFF not written */ /* 1936 */ {0x5, 0x80, 0x5}, /* DIFF not written */ /* 1939 */ {0x5, 0xe0, 0x6}, /* DIFF not written */ /* 1942 */ {0x5, 0x20, 0x7}, /* DIFF not written */ /* 1945 */ {0x5, 0xa0, 0x8}, /* DIFF not written */ /* 1948 */ {0x5, 0x0, 0x12}, /* DIFF not written */ /* 1951 */ {0x5, 0x2, 0xf}, /* DIFF not written */ /* 1954 */ {0x5, 0x10, 0x46}, /* DIFF not written */ /* 1957 */ {0x5, 0x8, 0x4a}, /* DIFF not written */ /* 1960 */ {0x3, 0x8, 0x3}, /* DIFF (0x3,0x28,0x3) */ /* 1963 */ {0x3, 0x8, 0x1}, /* 1966 */ {0x3, 0xc, 0x3}, /* DIFF not written */ /* 1969 */ {0x3, 0x21, 0x0}, /* DIFF (0x39) */ /* Extra block copied from init to hopefully ensure CCD is in a sane state */ /* 1837 */ {0x6, 0x10, 0x8}, /* 1840 */ {0x6, 0x0, 0x9}, /* 1843 */ {0x6, 0x0, 0x0a}, /* 1846 */ {0x6, 0x0, 0x0b}, /* 1849 */ {0x6, 0x10, 0xc}, /* 1852 */ {0x6, 0x0, 0xd}, /* 1855 */ {0x6, 0x0, 0xe}, /* 1858 */ {0x6, 0x0, 0xf}, /* 1861 */ {0x6, 0x10, 0x10}, /* 1864 */ {0x6, 0x2, 0x11}, /* 1867 */ {0x6, 0x0, 0x12}, /* 1870 */ {0x6, 0x4, 0x13}, /* 1873 */ {0x6, 0x2, 0x14}, /* 1876 */ {0x6, 0x8a, 0x51}, /* 1879 */ {0x6, 0x40, 0x52}, /* 1882 */ {0x6, 0xb6, 0x53}, /* 1885 */ {0x6, 0x3d, 0x54}, /* End of extra block */ /* 1972 */ {0x6, 0x3f, 0x1}, /* Block skipped */ /* 1975 */ {0x6, 0x10, 0x2}, /* 1978 */ {0x6, 0x64, 0x7}, /* 1981 */ {0x6, 0x10, 0x8}, /* 1984 */ {0x6, 0x0, 0x9}, /* 1987 */ {0x6, 0x0, 0xa}, /* 1990 */ {0x6, 0x0, 0xb}, /* 1993 */ {0x6, 0x10, 0xc}, /* 1996 */ {0x6, 0x0, 0xd}, /* 1999 */ {0x6, 0x0, 0xe}, /* 2002 */ {0x6, 0x0, 0xf}, /* 2005 */ {0x6, 0x10, 0x10}, /* 2008 */ {0x6, 0x2, 0x11}, /* 2011 */ {0x6, 0x0, 0x12}, /* 2014 */ {0x6, 0x4, 0x13}, /* 2017 */ {0x6, 0x2, 0x14}, /* 2020 */ {0x6, 0x8a, 0x51}, /* 2023 */ {0x6, 0x40, 0x52}, /* 2026 */ {0x6, 0xb6, 0x53}, /* 2029 */ {0x6, 0x3d, 0x54}, /* 2032 */ {0x6, 0x60, 0x57}, /* 2035 */ {0x6, 0x20, 0x58}, /* 2038 */ {0x6, 0x15, 0x59}, /* 2041 */ {0x6, 0x5, 0x5a}, /* 2044 */ {0x5, 0x1, 0xc0}, /* 2047 */ {0x5, 0x10, 0xcb}, /* 2050 */ {0x5, 0x80, 0xc1}, /* */ /* 2053 */ {0x5, 0x0, 0xc2}, /* 4 was 0 */ /* 2056 */ {0x5, 0x0, 0xca}, /* 2059 */ {0x5, 0x80, 0xc1}, /* */ /* 2062 */ {0x5, 0x4, 0xc2}, /* 2065 */ {0x5, 0x0, 0xca}, /* 2068 */ {0x5, 0x0, 0xc1}, /* */ /* 2071 */ {0x5, 0x0, 0xc2}, /* 2074 */ {0x5, 0x0, 0xca}, /* 2077 */ {0x5, 0x40, 0xc1}, /* */ /* 2080 */ {0x5, 0x17, 0xc2}, /* 2083 */ {0x5, 0x0, 0xca}, /* 2086 */ {0x5, 0x80, 0xc1}, /* */ /* 2089 */ {0x5, 0x6, 0xc2}, /* 2092 */ {0x5, 0x0, 0xca}, /* 2095 */ {0x5, 0x80, 0xc1}, /* */ /* 2098 */ {0x5, 0x4, 0xc2}, /* 2101 */ {0x5, 0x0, 0xca}, /* 2104 */ {0x3, 0x4c, 0x3}, /* 2107 */ {0x3, 0x18, 0x1}, /* 2110 */ {0x6, 0x70, 0x51}, /* 2113 */ {0x6, 0xbe, 0x53}, /* 2116 */ {0x6, 0x71, 0x57}, /* 2119 */ {0x6, 0x20, 0x58}, /* 2122 */ {0x6, 0x5, 0x59}, /* 2125 */ {0x6, 0x15, 0x5a}, /* 2128 */ {0x4, 0x0, 0x8}, /* Compress = OFF (0x1 to turn on) */ /* 2131 */ {0x4, 0x12, 0x9}, /* 2134 */ {0x4, 0x21, 0xa}, /* 2137 */ {0x4, 0x10, 0xb}, /* 2140 */ {0x4, 0x21, 0xc}, /* 2143 */ {0x4, 0x5, 0x0}, /* was 5 (Image Type ? ) */ /* 2146 */ {0x4, 0x0, 0x1}, /* 2149 */ {0x6, 0x3f, 0x1}, /* 2152 */ {0x4, 0x0, 0x4}, /* 2155 */ {0x4, 0x0, 0x5}, /* 2158 */ {0x4, 0x40, 0x6}, /* 2161 */ {0x4, 0x40, 0x7}, /* 2164 */ {0x6, 0x1c, 0x17}, /* 2167 */ {0x6, 0xe2, 0x19}, /* 2170 */ {0x6, 0x1c, 0x1b}, /* 2173 */ {0x6, 0xe2, 0x1d}, /* 2176 */ {0x6, 0xaa, 0x1f}, /* 2179 */ {0x6, 0x70, 0x20}, /* 2182 */ {0x5, 0x1, 0x10}, /* 2185 */ {0x5, 0x0, 0x11}, /* 2188 */ {0x5, 0x1, 0x0}, /* 2191 */ {0x5, 0x5, 0x1}, /* 2194 */ {0x5, 0x0, 0xc1}, /* */ /* 2197 */ {0x5, 0x0, 0xc2}, /* 2200 */ {0x5, 0x0, 0xca}, /* 2203 */ {0x6, 0x70, 0x51}, /* 2206 */ {0x6, 0xbe, 0x53}, {0, 0, 0} }; #if 0 /* * Data to initialize the camera in external video mode */ static __u16 spca505_open_data_ext[][3] = { /* line bmRequest,value,index */ /* External video input dataset */ /* 0808 */ {0x3, 0x4, 0x1}, /* 0809 */ {0x3, 0x0, 0x1}, /* 0810 */ {0x4, 0x50, 0x1}, /* 0811 */ {0x4, 0x0, 0x4}, /* 0812 */ {0x4, 0xa, 0x5}, /* 0813 */ {0x4, 0x20, 0x6}, /* 0814 */ {0x4, 0x20, 0x7}, /* 0815 */ {0x8, 0x4a, 0x0}, /* 0816 */ {0x5, 0x0, 0x10}, /* 0817 */ {0x5, 0x0, 0x11}, /* 0818 */ {0x3, 0x8, 0x3}, /* 0819 */ {0x3, 0x28, 0x3}, /* 0820 */ {0x3, 0x8, 0x1}, /* 0821 */ {0x3, 0x39, 0x0}, /* 0822 */ {0x5, 0x1, 0xc0}, /* 0823 */ {0x5, 0x10, 0xcb}, /* 0824 */ {0x5, 0x80, 0xc1}, /* 0825 */ {0x5, 0x5, 0xc2}, /* 0826 */ {0x5, 0x0, 0xca}, /* 0827 */ {0x5, 0x0, 0xc1}, /* 0828 */ {0x5, 0x1, 0xc2}, /* 0829 */ {0x5, 0x0, 0xca}, /* 0830 */ {0x5, 0x1, 0x10}, /* 0831 */ {0x5, 0xf, 0x11}, {0, 0, 0} }; /* * Additional data needed to initialze the camera in external mode */ static __u16 spca505_open_data2[][3] = { /* line bmRequest,value,index */ /* 1384 */ {0x3, 0x68, 0x3}, /* 1385 */ {0x3, 0x10, 0x1}, /* 1386 */ {0x8, 0x4a, 0x0}, /* 1387 */ {0x4, 0x0, 0x8}, /* was 1 COMPRESSION ENABLE ! */ /* 1388 */ {0x4, 0x12, 0x9}, /* Think these are the compression registers */ /* 1389 */ {0x4, 0x21, 0xa}, /* 1390 */ {0x4, 0x10, 0xb}, /* 1391 */ {0x4, 0x21, 0xc}, /* 1392 */ {0x4, 0x5, 0x0}, /* This may be the picture type code (5=160x120 as YUV4:2:0) */ /* 1393 */ {0x4, 0x0, 0x1}, /* 1394 */ {0x6, 0x3f, 0x1}, /* 1395 */ {0x4, 0x0, 0x4}, /* 1396 */ {0x4, 0xa, 0x5}, /* 1397 */ {0x4, 0x40, 0x6}, /* was 40 */ /* 1398 */ {0x4, 0x40, 0x7}, /* was 50 */ /* 1399 */ {0x4, 0x2, 0x5}, /* 1400 */ {0x4, 0x0, 0x4}, {0, 0, 0} }; #endif /* Made by Tomasz Zablocki (skalamandra@poczta.onet.pl) * SPCA505b chip based cameras initialization data * */ #define initial_brightness 0x0 //0x0(white)-0xff(black) /* * Data to initialize a SPCA505. Common to the CCD and external modes */ static __u16 spca505b_init_data[][3] = { //start {0x02, 0x00, 0x00}, //init {0x02, 0x00, 0x01}, {0x02, 0x00, 0x02}, {0x02, 0x00, 0x03}, {0x02, 0x00, 0x04}, {0x02, 0x00, 0x05}, {0x02, 0x00, 0x06}, {0x02, 0x00, 0x07}, {0x02, 0x00, 0x08}, {0x02, 0x00, 0x09}, {0x03, 0x00, 0x00}, {0x03, 0x00, 0x01}, {0x03, 0x00, 0x02}, {0x03, 0x00, 0x03}, {0x03, 0x00, 0x04}, {0x03, 0x00, 0x05}, {0x03, 0x00, 0x06}, {0x04, 0x00, 0x00}, {0x04, 0x00, 0x02}, {0x04, 0x00, 0x04}, {0x04, 0x00, 0x05}, {0x04, 0x00, 0x06}, {0x04, 0x00, 0x07}, {0x04, 0x00, 0x08}, {0x04, 0x00, 0x09}, {0x04, 0x00, 0x0a}, {0x04, 0x00, 0x0b}, {0x04, 0x00, 0x0c}, {0x07, 0x00, 0x00}, {0x07, 0x00, 0x03}, {0x08, 0x00, 0x00}, {0x08, 0x00, 0x01}, {0x08, 0x00, 0x02}, {0x00, 0x01, 0x00}, {0x00, 0x01, 0x01}, {0x00, 0x01, 0x34}, {0x00, 0x01, 0x35}, {0x06, 0x18, 0x08}, {0x06, 0xfc, 0x09}, {0x06, 0xfc, 0x0a}, {0x06, 0xfc, 0x0b}, {0x06, 0x18, 0x0c}, {0x06, 0xfc, 0x0d}, {0x06, 0xfc, 0x0e}, {0x06, 0xfc, 0x0f}, {0x06, 0x18, 0x10}, {0x06, 0xfe, 0x12}, {0x06, 0x00, 0x11}, {0x06, 0x00, 0x14}, {0x06, 0x00, 0x13}, {0x06, 0x28, 0x51}, {0x06, 0xff, 0x53}, {0x02, 0x00, 0x08}, {0x03, 0x00, 0x03}, {0x03, 0x10, 0x03}, {0, 0, 0} }; /* * Data to initialize the camera using the internal CCD */ static __u16 spca505b_open_data_ccd[][3] = { //{0x02,0x00,0x00}, {0x03, 0x04, 0x01}, //rst {0x03, 0x00, 0x01}, {0x03, 0x00, 0x00}, {0x03, 0x21, 0x00}, {0x03, 0x00, 0x04}, {0x03, 0x00, 0x03}, {0x03, 0x18, 0x03}, {0x03, 0x08, 0x01}, {0x03, 0x1c, 0x03}, {0x03, 0x5c, 0x03}, {0x03, 0x5c, 0x03}, {0x03, 0x18, 0x01}, //same as 505 {0x04, 0x10, 0x01}, {0x04, 0x00, 0x04}, {0x04, 0x00, 0x05}, {0x04, 0x20, 0x06}, {0x04, 0x20, 0x07}, {0x08, 0x0a, 0x00}, {0x05, 0x00, 0x10}, {0x05, 0x00, 0x11}, {0x05, 0x00, 0x12}, {0x05, 0x6f, 0x00}, {0x05, initial_brightness >> 6, 0x00}, {0x05, initial_brightness << 2, 0x01}, {0x05, 0x00, 0x02}, {0x05, 0x01, 0x03}, {0x05, 0x00, 0x04}, {0x05, 0x03, 0x05}, {0x05, 0xe0, 0x06}, {0x05, 0x20, 0x07}, {0x05, 0xa0, 0x08}, {0x05, 0x00, 0x12}, {0x05, 0x02, 0x0f}, {0x05, 128, 0x14}, //max exposure off (0=on) {0x05, 0x01, 0xb0}, {0x05, 0x01, 0xbf}, {0x03, 0x02, 0x06}, {0x05, 0x10, 0x46}, {0x05, 0x08, 0x4a}, {0x06, 0x00, 0x01}, {0x06, 0x10, 0x02}, {0x06, 0x64, 0x07}, {0x06, 0x18, 0x08}, {0x06, 0xfc, 0x09}, {0x06, 0xfc, 0x0a}, {0x06, 0xfc, 0x0b}, {0x04, 0x00, 0x01}, {0x06, 0x18, 0x0c}, {0x06, 0xfc, 0x0d}, {0x06, 0xfc, 0x0e}, {0x06, 0xfc, 0x0f}, {0x06, 0x11, 0x10}, //contrast {0x06, 0x00, 0x11}, {0x06, 0xfe, 0x12}, {0x06, 0x00, 0x13}, {0x06, 0x00, 0x14}, {0x06, 0x9d, 0x51}, {0x06, 0x40, 0x52}, {0x06, 0x7c, 0x53}, {0x06, 0x40, 0x54}, {0x06, 0x02, 0x57}, {0x06, 0x03, 0x58}, {0x06, 0x15, 0x59}, {0x06, 0x05, 0x5a}, {0x06, 0x03, 0x56}, {0x06, 0x02, 0x3f}, {0x06, 0x00, 0x40}, {0x06, 0x39, 0x41}, {0x06, 0x69, 0x42}, {0x06, 0x87, 0x43}, {0x06, 0x9e, 0x44}, {0x06, 0xb1, 0x45}, {0x06, 0xbf, 0x46}, {0x06, 0xcc, 0x47}, {0x06, 0xd5, 0x48}, {0x06, 0xdd, 0x49}, {0x06, 0xe3, 0x4a}, {0x06, 0xe8, 0x4b}, {0x06, 0xed, 0x4c}, {0x06, 0xf2, 0x4d}, {0x06, 0xf7, 0x4e}, {0x06, 0xfc, 0x4f}, {0x06, 0xff, 0x50}, {0x05, 0x01, 0xc0}, {0x05, 0x10, 0xcb}, {0x05, 0x40, 0xc1}, {0x05, 0x4, 0xc2}, {0x05, 0x00, 0xca}, {0x05, 0x40, 0xc1}, {0x05, 0x09, 0xc2}, {0x05, 0x00, 0xca}, {0x05, 0xc0, 0xc1}, {0x05, 0x09, 0xc2}, {0x05, 0x00, 0xca}, {0x05, 0x40, 0xc1}, {0x05, 0x59, 0xc2}, {0x05, 0x00, 0xca}, {0x04, 0x00, 0x01}, {0x05, 0x80, 0xc1}, {0x05, 0xec, 0xc2}, {0x05, 0x0, 0xca}, {0x06, 0x02, 0x57}, {0x06, 0x01, 0x58}, {0x06, 0x15, 0x59}, {0x06, 0x0a, 0x5a}, {0x06, 0x01, 0x57}, {0x06, 0x8a, 0x03}, {0x06, 0x0a, 0x6c}, {0x06, 0x30, 0x01}, {0x06, 0x20, 0x02}, {0x06, 0x00, 0x03}, {0x05, 0x8c, 0x25}, {0x06, 0x4d, 0x51}, //maybe saturation (4d) {0x06, 0x84, 0x53}, //making green (84) {0x06, 0x0, 0x57}, //sharpness (1) {0x06, 0x18, 0x08}, {0x06, 0xfc, 0x09}, {0x06, 0xfc, 0x0a}, {0x06, 0xfc, 0x0b}, {0x06, 0x18, 0x0c}, //maybe hue (18) {0x06, 0xfc, 0x0d}, {0x06, 0xfc, 0x0e}, {0x06, 0xfc, 0x0f}, {0x06, 0x18, 0x10}, //maybe contrast (18) {0x05, 0x01, 0x02}, {0x04, 0x0, 0x08}, //compression {0x04, 0x12, 0x09}, {0x04, 0x21, 0x0a}, {0x04, 0x10, 0x0b}, {0x04, 0x21, 0x0c}, {0x04, 0x1d, 0x00}, //imagetype (1d) {0x04, 0x41, 0x01}, //hardware snapcontrol {0x04, 0x00, 0x04}, {0x04, 0x00, 0x05}, {0x04, 0x10, 0x06}, {0x04, 0x10, 0x07}, {0x04, 0x40, 0x06}, {0x04, 0x40, 0x07}, {0x04, 0x00, 0x04}, {0x04, 0x00, 0x05}, {0x06, 0x1c, 0x17}, {0x06, 0xe2, 0x19}, {0x06, 0x1c, 0x1b}, {0x06, 0xe2, 0x1d}, {0x06, 0x5f, 0x1f}, {0x06, 0x32, 0x20}, {0x05, initial_brightness >> 6, 0x00}, {0x05, initial_brightness << 2, 0x01}, {0x05, 0x06, 0xc1}, {0x05, 0x58, 0xc2}, {0x05, 0x0, 0xca}, {0x05, 0x0, 0x11}, {0, 0, 0} }; #endif /* SPCA505_INIT_H */ //eof pwcbsd/spca5xx-20060402/drivers/usb/spca506.h000755 000423 000000 00000050316 10552522126 020675 0ustar00luigiwheel000000 000000 /* * SPCA506 chip based cameras function * M Xhaard 15/04/2004 based on different work Mark Taylor and others * and my own snoopy file on a pv-321c donate by a german compagny * "Firma Frank Gmbh" from Saarbruecken */ #ifndef SPCA506_INIT_H #define SPCA506_INIT_H #define SAA7113_bright 0x0A // defaults 0x80 #define SAA7113_contrast 0x0B // defaults 0x47 #define SAA7113_saturation 0x0C //defaults 0x40 #define SAA7113_hue 0x0D //defaults 0x00 /* define from v4l */ //#define VIDEO_MODE_PAL 0 //#define VIDEO_MODE_NTSC 1 //#define VIDEO_MODE_SECAM 2 //#define VIDEO_MODE_AUTO 3 /*********************** Specific spca506 Usbgrabber ************************/ static void spca506_init(struct usb_spca50x *spca50x); static void spca506_start(struct usb_spca50x *spca50x); static void spca506_stop(struct usb_spca50x *spca50x); static void spca506_Setsize(struct usb_spca50x *spca50x, __u16 code, __u16 xmult, __u16 ymult); static void spca506_SetBrightContrastHueColors(struct usb_spca50x *spca50x, __u16 bright, __u16 contrast, __u16 hue, __u16 colors); static void spca506_GetBrightContrastHueColors(struct usb_spca50x *spca50x, __u16 * bright, __u16 * contrast, __u16 * hue, __u16 * colors); static void spca506_SetNormeInput(struct usb_spca50x *spca50x, __u16 norme, __u16 channel); static void spca506_GetNormeInput(struct usb_spca50x *spca50x, __u16 * norme, __u16 * channel); /****************************************************************************/ static void spca506_Initi2c(struct usb_spca50x *spca50x) { usb_wr_vend_dev(spca50x->dev, 0x07, SAA7113_I2C_BASE_WRITE, 0x0004, NULL, 0); } static void spca506_WriteI2c(struct usb_spca50x *spca50x, __u16 valeur, __u16 registre) { int retry = 60; unsigned char Data[2]; usb_wr_vend_dev(spca50x->dev, 0x07, registre, 0x0001, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0x07, valeur, 0x0000, NULL, 0); while (retry--) { usb_rd_vend_dev(spca50x->dev, 0x07, 0, 0x0003, Data, 2); if ((Data[0] | Data[1]) == 0x00) break; } } static int spca506_ReadI2c(struct usb_spca50x *spca50x, __u16 registre) { int retry = 60; unsigned char Data[2]; unsigned char value = 0; usb_wr_vend_dev(spca50x->dev, 0x07, SAA7113_I2C_BASE_WRITE, 0x0004, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0x07, registre, 0x0001, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0x07, 0x01, 0x0002, NULL, 0); while (retry--) { usb_rd_vend_dev(spca50x->dev, 0x07, 0, 0x0003, Data, 2); if ((Data[0] | Data[1]) == 0x00) break; } if (retry == 0) return -1; usb_rd_vend_dev(spca50x->dev, 0x07, 0, 0x0000, &value, 1); return (int) value; } static void spca506_SetNormeInput(struct usb_spca50x *spca50x, __u16 norme, __u16 channel) { __u8 setbit0 = 0x00; __u8 setbit1 = 0x00; __u8 videomask = 0x00; PDEBUG(3, "************ Open Set Norme **************"); spca506_Initi2c(spca50x); /* NTSC bit0 -> 1(525 l) PAL SECAM bit0 -> 0 (625 l) */ /* Composite channel bit1 -> 1 S-video bit 1 -> 0 */ /* and exclude SAA7113 reserved channel set default 0 otherwise */ if (norme == VIDEO_MODE_NTSC) setbit0 = 0x01; if ((channel == 4) || (channel == 5) || (channel > 9)) channel = 0; if (channel < 4) setbit1 = 0x02; videomask = (0x48 | setbit0 | setbit1); usb_wr_vend_dev(spca50x->dev, 0x08, videomask, 0x0000, NULL, 0); spca506_WriteI2c(spca50x, (0xc0 | (channel & 0x0F)), 0x02); switch (norme) { case VIDEO_MODE_PAL: spca506_WriteI2c(spca50x, 0x03, 0x0e); //Chrominance Control PAL BGHIV break; case VIDEO_MODE_NTSC: spca506_WriteI2c(spca50x, 0x33, 0x0e); //Chrominance Control NTSC N break; case VIDEO_MODE_SECAM: spca506_WriteI2c(spca50x, 0x53, 0x0e); //Chrominance Control SECAM break; default: spca506_WriteI2c(spca50x, 0x03, 0x0e); //Chrominance Control PAL BGHIV break; } spca50x->norme = norme; spca50x->channel = channel; PDEBUG(3, "Set Video Byte to 0x%2X ", videomask); PDEBUG(3, "Set Norme : %d Channel %d ", norme, channel); PDEBUG(3, "************ Close SetNorme **************"); } static void spca506_GetNormeInput(struct usb_spca50x *spca50x, __u16 * norme, __u16 * channel) { PDEBUG(3, "************ Open Get Norme **************"); /* Read the register is not so good value change so we use your own copy in spca50x struct */ *norme = spca50x->norme; *channel = spca50x->channel; PDEBUG(3, "Get Norme : %d Channel %d ", *norme, *channel); PDEBUG(3, "************ Close Get Norme **************"); } static void spca506_init(struct usb_spca50x *spca50x) { PDEBUG(3, "************ Open Init spca506 **************"); usb_wr_vend_dev(spca50x->dev, 0x03, 0x00, 0x0004, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0x03, 0xFF, 0x0003, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0x03, 0x00, 0x0000, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0x03, 0x1c, 0x0001, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0x03, 0x18, 0x0001, NULL, 0); /* Init on PAL and composite input0 */ spca506_SetNormeInput(spca50x, 0, 0); usb_wr_vend_dev(spca50x->dev, 0x03, 0x1c, 1, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0x03, 0x18, 0x0001, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0x05, 0x00, 0x0000, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0x05, 0xef, 0x0001, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0x05, 0x00, 0x00c1, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0x05, 0x00, 0x00c2, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0x06, 0x18, 0x0002, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0x06, 0xf5, 0x0011, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0x06, 0x02, 0x0012, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0x06, 0xfb, 0x0013, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0x06, 0x00, 0x0014, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0x06, 0xa4, 0x0051, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0x06, 0x40, 0x0052, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0x06, 0x71, 0x0053, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0x06, 0x40, 0x0054, NULL, 0); /***********************************************************/ usb_wr_vend_dev(spca50x->dev, 0x03, 0x00, 0x0004, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0x03, 0x00, 0x0003, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0x03, 0x00, 0x0004, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0x03, 0xFF, 0x0003, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0x02, 0x00, 0x0000, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0x03, 0x60, 0x0000, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0x03, 0x18, 0x0001, NULL, 0); /* for a better reading mx :) */ /*spca506_WriteI2c(value,register) */ spca506_Initi2c(spca50x); spca506_WriteI2c(spca50x, 0x08, 0x01); spca506_WriteI2c(spca50x, 0xc0, 0x02); // input composite video spca506_WriteI2c(spca50x, 0x33, 0x03); spca506_WriteI2c(spca50x, 0x00, 0x04); spca506_WriteI2c(spca50x, 0x00, 0x05); spca506_WriteI2c(spca50x, 0x0d, 0x06); spca506_WriteI2c(spca50x, 0xf0, 0x07); spca506_WriteI2c(spca50x, 0x98, 0x08); spca506_WriteI2c(spca50x, 0x03, 0x09); spca506_WriteI2c(spca50x, 0x80, 0x0a); spca506_WriteI2c(spca50x, 0x47, 0x0b); spca506_WriteI2c(spca50x, 0x48, 0x0c); spca506_WriteI2c(spca50x, 0x00, 0x0d); spca506_WriteI2c(spca50x, 0x03, 0x0e); // Chroma Pal adjust spca506_WriteI2c(spca50x, 0x2a, 0x0f); spca506_WriteI2c(spca50x, 0x00, 0x10); spca506_WriteI2c(spca50x, 0x0c, 0x11); spca506_WriteI2c(spca50x, 0xb8, 0x12); spca506_WriteI2c(spca50x, 0x01, 0x13); spca506_WriteI2c(spca50x, 0x00, 0x14); spca506_WriteI2c(spca50x, 0x00, 0x15); spca506_WriteI2c(spca50x, 0x00, 0x16); spca506_WriteI2c(spca50x, 0x00, 0x17); spca506_WriteI2c(spca50x, 0x00, 0x18); spca506_WriteI2c(spca50x, 0x00, 0x19); spca506_WriteI2c(spca50x, 0x00, 0x1a); spca506_WriteI2c(spca50x, 0x00, 0x1b); spca506_WriteI2c(spca50x, 0x00, 0x1c); spca506_WriteI2c(spca50x, 0x00, 0x1d); spca506_WriteI2c(spca50x, 0x00, 0x1e); spca506_WriteI2c(spca50x, 0xa1, 0x1f); spca506_WriteI2c(spca50x, 0x02, 0x40); spca506_WriteI2c(spca50x, 0xff, 0x41); spca506_WriteI2c(spca50x, 0xff, 0x42); spca506_WriteI2c(spca50x, 0xff, 0x43); spca506_WriteI2c(spca50x, 0xff, 0x44); spca506_WriteI2c(spca50x, 0xff, 0x45); spca506_WriteI2c(spca50x, 0xff, 0x46); spca506_WriteI2c(spca50x, 0xff, 0x47); spca506_WriteI2c(spca50x, 0xff, 0x48); spca506_WriteI2c(spca50x, 0xff, 0x49); spca506_WriteI2c(spca50x, 0xff, 0x4a); spca506_WriteI2c(spca50x, 0xff, 0x4b); spca506_WriteI2c(spca50x, 0xff, 0x4c); spca506_WriteI2c(spca50x, 0xff, 0x4d); spca506_WriteI2c(spca50x, 0xff, 0x4e); spca506_WriteI2c(spca50x, 0xff, 0x4f); spca506_WriteI2c(spca50x, 0xff, 0x50); spca506_WriteI2c(spca50x, 0xff, 0x51); spca506_WriteI2c(spca50x, 0xff, 0x52); spca506_WriteI2c(spca50x, 0xff, 0x53); spca506_WriteI2c(spca50x, 0xff, 0x54); spca506_WriteI2c(spca50x, 0xff, 0x55); spca506_WriteI2c(spca50x, 0xff, 0x56); spca506_WriteI2c(spca50x, 0xff, 0x57); spca506_WriteI2c(spca50x, 0x00, 0x58); spca506_WriteI2c(spca50x, 0x54, 0x59); spca506_WriteI2c(spca50x, 0x07, 0x5a); spca506_WriteI2c(spca50x, 0x83, 0x5b); spca506_WriteI2c(spca50x, 0x00, 0x5c); spca506_WriteI2c(spca50x, 0x00, 0x5d); spca506_WriteI2c(spca50x, 0x00, 0x5e); spca506_WriteI2c(spca50x, 0x00, 0x5f); spca506_WriteI2c(spca50x, 0x00, 0x60); spca506_WriteI2c(spca50x, 0x05, 0x61); spca506_WriteI2c(spca50x, 0x9f, 0x62); PDEBUG(3, "************ Close Init spca506 **************"); } static void spca506_start(struct usb_spca50x *spca50x) { __u16 norme = 0; __u16 channel = 0; unsigned char Data[2]; PDEBUG(3, "************ Open Start spca506 **************"); /***********************************************************/ usb_wr_vend_dev(spca50x->dev, 0x03, 0x00, 0x0004, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0x03, 0x00, 0x0003, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0x03, 0x00, 0x0004, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0x03, 0xFF, 0x0003, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0x02, 0x00, 0x0000, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0x03, 0x60, 0x0000, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0x03, 0x18, 0x0001, NULL, 0); /*spca506_WriteI2c(value,register) */ spca506_Initi2c(spca50x); spca506_WriteI2c(spca50x, 0x08, 0x01); //Increment Delay //spca506_WriteI2c(spca50x,0xc0,0x02);//Analog Input Control 1 spca506_WriteI2c(spca50x, 0x33, 0x03); //Analog Input Control 2 spca506_WriteI2c(spca50x, 0x00, 0x04); //Analog Input Control 3 spca506_WriteI2c(spca50x, 0x00, 0x05); //Analog Input Control 4 spca506_WriteI2c(spca50x, 0x0d, 0x06); //Horizontal Sync Start 0xe9-0x0d spca506_WriteI2c(spca50x, 0xf0, 0x07); //Horizontal Sync Stop 0x0d-0xf0 spca506_WriteI2c(spca50x, 0x98, 0x08); //Sync Control /* Defaults value */ spca506_WriteI2c(spca50x, 0x03, 0x09); //Luminance Control spca506_WriteI2c(spca50x, 0x80, 0x0a); //Luminance Brightness spca506_WriteI2c(spca50x, 0x47, 0x0b); //Luminance Contrast spca506_WriteI2c(spca50x, 0x48, 0x0c); //Chrominance Saturation spca506_WriteI2c(spca50x, 0x00, 0x0d); //Chrominance Hue Control spca506_WriteI2c(spca50x, 0x2a, 0x0f); //Chrominance Gain Control /*************************************************************/ spca506_WriteI2c(spca50x, 0x00, 0x10); //Format/Delay Control spca506_WriteI2c(spca50x, 0x0c, 0x11); //Output Control 1 spca506_WriteI2c(spca50x, 0xb8, 0x12); //Output Control 2 spca506_WriteI2c(spca50x, 0x01, 0x13); //Output Control 3 spca506_WriteI2c(spca50x, 0x00, 0x14); //reserved spca506_WriteI2c(spca50x, 0x00, 0x15); //VGATE START spca506_WriteI2c(spca50x, 0x00, 0x16); //VGATE STOP spca506_WriteI2c(spca50x, 0x00, 0x17); //VGATE Control (MSB) spca506_WriteI2c(spca50x, 0x00, 0x18); spca506_WriteI2c(spca50x, 0x00, 0x19); spca506_WriteI2c(spca50x, 0x00, 0x1a); spca506_WriteI2c(spca50x, 0x00, 0x1b); spca506_WriteI2c(spca50x, 0x00, 0x1c); spca506_WriteI2c(spca50x, 0x00, 0x1d); spca506_WriteI2c(spca50x, 0x00, 0x1e); spca506_WriteI2c(spca50x, 0xa1, 0x1f); spca506_WriteI2c(spca50x, 0x02, 0x40); spca506_WriteI2c(spca50x, 0xff, 0x41); spca506_WriteI2c(spca50x, 0xff, 0x42); spca506_WriteI2c(spca50x, 0xff, 0x43); spca506_WriteI2c(spca50x, 0xff, 0x44); spca506_WriteI2c(spca50x, 0xff, 0x45); spca506_WriteI2c(spca50x, 0xff, 0x46); spca506_WriteI2c(spca50x, 0xff, 0x47); spca506_WriteI2c(spca50x, 0xff, 0x48); spca506_WriteI2c(spca50x, 0xff, 0x49); spca506_WriteI2c(spca50x, 0xff, 0x4a); spca506_WriteI2c(spca50x, 0xff, 0x4b); spca506_WriteI2c(spca50x, 0xff, 0x4c); spca506_WriteI2c(spca50x, 0xff, 0x4d); spca506_WriteI2c(spca50x, 0xff, 0x4e); spca506_WriteI2c(spca50x, 0xff, 0x4f); spca506_WriteI2c(spca50x, 0xff, 0x50); spca506_WriteI2c(spca50x, 0xff, 0x51); spca506_WriteI2c(spca50x, 0xff, 0x52); spca506_WriteI2c(spca50x, 0xff, 0x53); spca506_WriteI2c(spca50x, 0xff, 0x54); spca506_WriteI2c(spca50x, 0xff, 0x55); spca506_WriteI2c(spca50x, 0xff, 0x56); spca506_WriteI2c(spca50x, 0xff, 0x57); spca506_WriteI2c(spca50x, 0x00, 0x58); spca506_WriteI2c(spca50x, 0x54, 0x59); spca506_WriteI2c(spca50x, 0x07, 0x5a); spca506_WriteI2c(spca50x, 0x83, 0x5b); spca506_WriteI2c(spca50x, 0x00, 0x5c); spca506_WriteI2c(spca50x, 0x00, 0x5d); spca506_WriteI2c(spca50x, 0x00, 0x5e); spca506_WriteI2c(spca50x, 0x00, 0x5f); spca506_WriteI2c(spca50x, 0x00, 0x60); spca506_WriteI2c(spca50x, 0x05, 0x61); spca506_WriteI2c(spca50x, 0x9f, 0x62); /***********************************************************/ usb_wr_vend_dev(spca50x->dev, 0x05, 0x00, 0x0003, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0x05, 0x00, 0x0004, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0x03, 0x10, 0x0001, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0x03, 0x78, 0x0000, NULL, 0); /* compress setting and size */ /* set i2c luma */ usb_wr_vend_dev(spca50x->dev, 0x02, 0x01, 0x0000, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0x03, 0x12, 0x0001, NULL, 0); usb_rd_vend_dev(spca50x->dev, 0x04, 0, 0x0001, Data, 2); PDEBUG(3, "************ Close Start spca506 **************"); spca506_GetNormeInput(spca50x, &norme, &channel); spca506_SetNormeInput(spca50x, norme, channel); } static void spca506_stop(struct usb_spca50x *spca50x) { usb_wr_vend_dev(spca50x->dev, 0x02, 0x00, 0x0000, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0x03, 0x00, 0x0004, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0x03, 0x00, 0x0003, NULL, 0); } static void spca506_Setsize(struct usb_spca50x *spca50x, __u16 code, __u16 xmult, __u16 ymult) { PDEBUG(3, "************ Open SetSize spca506 **************"); usb_wr_vend_dev(spca50x->dev, 0x04, (0x18 | (code & 0x07)), 0x0000, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0x04, 0x41, 0x0001, NULL, 0); // Soft snap 0x40 Hard 0x41 usb_wr_vend_dev(spca50x->dev, 0x04, 0x00, 0x0002, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0x04, 0x00, 0x0003, NULL, 0); //reserved usb_wr_vend_dev(spca50x->dev, 0x04, 0x00, 0x0004, NULL, 0); //reserved usb_wr_vend_dev(spca50x->dev, 0x04, 0x01, 0x0005, NULL, 0); //reserved usb_wr_vend_dev(spca50x->dev, 0x04, xmult, 0x0006, NULL, 0); //reserced usb_wr_vend_dev(spca50x->dev, 0x04, ymult, 0x0007, NULL, 0); //reserved usb_wr_vend_dev(spca50x->dev, 0x04, 0x00, 0x0008, NULL, 0); // compression 1 usb_wr_vend_dev(spca50x->dev, 0x04, 0x00, 0x0009, NULL, 0); //T=64 -> 2 usb_wr_vend_dev(spca50x->dev, 0x04, 0x21, 0x000a, NULL, 0); //threshold2D usb_wr_vend_dev(spca50x->dev, 0x04, 0x00, 0x000b, NULL, 0); //quantization PDEBUG(3, "************ Close SetSize spca506 **************"); } static void spca506_SetBrightContrastHueColors(struct usb_spca50x *spca50x, __u16 bright, __u16 contrast, __u16 hue, __u16 colors) { spca506_Initi2c(spca50x); spca506_WriteI2c(spca50x, ((bright >> 8) & 0xFF), SAA7113_bright); spca506_WriteI2c(spca50x, ((contrast >> 8) & 0xFF), SAA7113_contrast); spca506_WriteI2c(spca50x, ((hue >> 8) & 0xFF), SAA7113_hue); spca506_WriteI2c(spca50x, ((colors >> 8) & 0xFF), SAA7113_saturation); spca506_WriteI2c(spca50x, 0x01, 0x09); } static void spca506_GetBrightContrastHueColors(struct usb_spca50x *spca50x, __u16 * bright, __u16 * contrast, __u16 * hue, __u16 * colors) { *bright = (spca506_ReadI2c(spca50x, SAA7113_bright)) << 8; *contrast = (spca506_ReadI2c(spca50x, SAA7113_contrast)) << 8; *hue = (spca506_ReadI2c(spca50x, SAA7113_hue)) << 8; *colors = (spca506_ReadI2c(spca50x, SAA7113_saturation)) << 8; } /************** old code from spca50x *********************************/ /********************************************************************** * * Camera interface * **********************************************************************/ /* Read a value from the I2C bus. Returns the value read */ static int spca50x_read_i2c(struct usb_spca50x *spca50x, __u16 device, __u16 address) { struct usb_device *dev = spca50x->dev; int err_code; int retry; int ctrl = spca50x->i2c_ctrl_reg; //The I2C control register int base = spca50x->i2c_base; //The I2C base address err_code = spca50x_reg_write(dev, ctrl, base + SPCA50X_I2C_DEVICE, device); err_code = spca50x_reg_write(dev, ctrl, base + SPCA50X_I2C_SUBADDR, address); err_code = spca50x_reg_write(dev, ctrl, base + SPCA50X_I2C_TRIGGER, SPCA50X_I2C_TRIGGER_BIT); /* Hmm. 506 docs imply we should poll the ready register before reading the return value */ /* Poll the status register for a ready status */ /* Doesn't look like the windows driver does tho' */ retry = 60; while (--retry) { err_code = spca50x_reg_read(dev, ctrl, base + SPCA50X_I2C_STATUS, 1); if (err_code < 0) PDEBUG(1, "Error reading I2C status register"); if (!err_code) break; } if (!retry) PDEBUG(1, "Too many retries polling I2C status after write to register"); err_code = spca50x_reg_read(dev, ctrl, base + SPCA50X_I2C_READ, 1); if (err_code < 0) PDEBUG(1, "Failed to read I2C register at %d:%d", device, address); PDEBUG(3, "Read %d from %d:%d", err_code, device, address); return err_code; } static int spca50x_write_i2c(struct usb_spca50x *spca50x, __u16 device, __u16 subaddress, __u16 data) { struct usb_device *dev = spca50x->dev; int err_code; int retry; int ctrl = spca50x->i2c_ctrl_reg; //The I2C control register int base = spca50x->i2c_base; //The I2C base address /* Tell the SPCA50x i2c subsystem the device address of the i2c device */ err_code = spca50x_reg_write(dev, ctrl, base + SPCA50X_I2C_DEVICE, device); /* Poll the status register for a ready status */ retry = 60; // Arbitrary while (--retry) { err_code = spca50x_reg_read(dev, ctrl, base + SPCA50X_I2C_STATUS, 1); if (err_code < 0) PDEBUG(1, "Error reading I2C status register"); if (!err_code) break; } if (!retry) PDEBUG(1, "Too many retries polling I2C status"); err_code = spca50x_reg_write(dev, ctrl, base + SPCA50X_I2C_SUBADDR, subaddress); err_code = spca50x_reg_write(dev, ctrl, base + SPCA50X_I2C_VALUE, data); if (spca50x->i2c_trigger_on_write) err_code = spca50x_reg_write(dev, ctrl, base + SPCA50X_I2C_TRIGGER, SPCA50X_I2C_TRIGGER_BIT); /* Poll the status register for a ready status */ retry = 60; while (--retry) { err_code = spca50x_reg_read(dev, ctrl, SPCA50X_I2C_STATUS, 2); if (err_code < 0) PDEBUG(1, "Error reading I2C status register"); if (!err_code) break; } if (!retry) PDEBUG(1, "Too many retries polling I2C status after write to register"); if (debug > 2) { err_code = spca50x_read_i2c(spca50x, device, subaddress); if (err_code < 0) { PDEBUG(3, "Can't read back I2C register value for %d:%d", device, subaddress); } else if ((err_code & 0xff) != (data & 0xff)) PDEBUG(3, "Read back %x should be %x at subaddr %x", err_code, data, subaddress); } return 0; } #endif /* SPCA506_INIT_H */ //eof pwcbsd/spca5xx-20060402/drivers/usb/spca508_init.h000744 000423 000000 00000151353 10546676142 021736 0ustar00luigiwheel000000 000000 /* * SPCA508 chip based cameras initialization data * */ #ifndef SPCA508_INIT_H #define SPCA508_INIT_H /* Frame packet header offsets for the spca508 */ #define SPCA508_OFFSET_TYPE 1 #define SPCA508_OFFSET_COMPRESS 2 #define SPCA508_OFFSET_FRAMSEQ 8 #define SPCA508_OFFSET_WIN1LUM 11 #define SPCA508_OFFSET_DATA 37 #define SPCA508_SNAPBIT 0x20 #define SPCA508_SNAPCTRL 0x40 /*************** I2c ****************/ #define SPCA508_INDEX_I2C_BASE 0x8800 /* Initialization data: this is the first set-up data written to the device (before the open data). */ static __u16 spca508_init_data[][3] = #define IGN(x) /* nothing */ { /* line URB req, value, index */ /* 22 1 *//* READ { 0, 0x0 IGN(URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE:), 0x00 } -> 0000: 12 01 10 01 00 00 00 08 33 07 10 01 00 00 01 02 0010: 00 01 */ /* 44 2 *//* READ { 0, 0x0 IGN(URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE:), 0x00 } -> 0000: 09 02 89 00 01 01 00 80 32 09 04 00 00 01 ff 00 0010: 00 00 07 05 81 01 00 00 01 09 04 00 01 01 ff 00 0020: 00 00 07 05 81 01 80 00 01 09 04 00 02 01 ff 00 0030: 00 00 07 05 81 01 80 01 01 09 04 00 03 01 ff 00 */ /* 68 3 *//* READ { 0, 0x0 IGN(URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE:), 0x00 } -> 0000: 09 02 89 00 01 01 00 80 32 09 04 00 00 01 ff 00 0010: 00 00 07 05 81 01 00 00 01 09 04 00 01 01 ff 00 0020: 00 00 07 05 81 01 80 00 01 09 04 00 02 01 ff 00 0030: 00 00 07 05 81 01 80 01 01 09 04 00 03 01 ff 00 0040: 00 00 07 05 81 01 00 02 01 09 04 00 04 01 ff 00 0050: 00 00 07 05 81 01 80 02 01 09 04 00 05 01 ff 00 0060: 00 00 07 05 81 01 00 03 01 09 04 00 06 01 ff 00 0070: 00 00 07 05 81 01 80 03 01 09 04 00 07 01 ff 00 0080: 00 00 07 05 81 01 ff 03 01 */ /* 104 4 *//* UNKNOWN DIRECTION (URB_FUNCTION_SELECT_CONFIGURATION:) */ /* 44274 1804 */ {0, 0x0000, 0x870b}, // /* 44299 1805 */ {0, 0x0020, 0x8112}, // Video drop enable, ISO streaming disable /* 44324 1806 */ {0, 0x0003, 0x8111}, // Reset compression & memory /* 44349 1807 */ {0, 0x0000, 0x8110}, // Disable all outputs /* 44372 1808 *//* READ { 0, 0x0000, 0x8114 } -> 0000: 00 */ /* 44398 1809 */ {0, 0x0000, 0x8114}, // SW GPIO data /* 44423 1810 */ {0, 0x0008, 0x8110}, // Enable charge pump output /* 44527 1811 */ {0, 0x0002, 0x8116}, // 200 kHz pump clock /* 44555 1812 *//* UNKNOWN DIRECTION (URB_FUNCTION_SELECT_INTERFACE:) */ /* 44590 1813 */ {0, 0x0003, 0x8111}, // Reset compression & memory /* 44615 1814 */ {0, 0x0000, 0x8111}, // Normal mode (not reset) /* 44640 1815 */ {0, 0x0098, 0x8110}, // Enable charge pump output, sync.serial,external 2x clock /* 44665 1816 */ {0, 0x000d, 0x8114}, // SW GPIO data /* 44690 1817 */ {0, 0x0002, 0x8116}, // 200 kHz pump clock /* 44715 1818 */ {0, 0x0020, 0x8112}, // Video drop enable, ISO streaming disable // --------------------------------------- /* 44740 1819 */ {0, 0x000f, 0x8402}, // memory bank /* 44765 1820 */ {0, 0x0000, 0x8403}, // ... address // --------------------------------------- // 0x88__ is Synchronous Serial Interface. // TBD: This table could be expressed more compactly // using spca508_write_i2c_vector(). // TBD: Should see if the values in spca50x_i2c_data // would work with the VQ110 instead of the values // below. /* 44790 1821 */ {0, 0x00c0, 0x8804}, // SSI slave addr /* 44815 1822 */ {0, 0x0008, 0x8802}, // 375 Khz SSI clock /* 44838 1823 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 44862 1824 *//* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ /* 44888 1825 */ {0, 0x0008, 0x8802}, // 375 Khz SSI clock /* 44913 1826 */ {0, 0x0012, 0x8801}, // SSI reg addr /* 44938 1827 */ {0, 0x0080, 0x8800}, // SSI data to write /* 44961 1828 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 44985 1829 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 45009 1830 *//* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ /* 45035 1831 */ {0, 0x0008, 0x8802}, // 375 Khz SSI clock /* 45060 1832 */ {0, 0x0012, 0x8801}, // SSI reg addr /* 45085 1833 */ {0, 0x0000, 0x8800}, // SSI data to write /* 45108 1834 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 45132 1835 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 45156 1836 *//* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ /* 45182 1837 */ {0, 0x0008, 0x8802}, // 375 Khz SSI clock /* 45207 1838 */ {0, 0x0011, 0x8801}, // SSI reg addr /* 45232 1839 */ {0, 0x0040, 0x8800}, // SSI data to write /* 45255 1840 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 45279 1841 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 45303 1842 *//* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ /* 45329 1843 */ {0, 0x0008, 0x8802}, /* 45354 1844 */ {0, 0x0013, 0x8801}, /* 45379 1845 */ {0, 0x0000, 0x8800}, /* 45402 1846 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 45426 1847 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 45450 1848 *//* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ /* 45476 1849 */ {0, 0x0008, 0x8802}, /* 45501 1850 */ {0, 0x0014, 0x8801}, /* 45526 1851 */ {0, 0x0000, 0x8800}, /* 45549 1852 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 45573 1853 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 45597 1854 *//* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ /* 45623 1855 */ {0, 0x0008, 0x8802}, /* 45648 1856 */ {0, 0x0015, 0x8801}, /* 45673 1857 */ {0, 0x0001, 0x8800}, /* 45696 1858 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 45720 1859 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 45744 1860 *//* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ /* 45770 1861 */ {0, 0x0008, 0x8802}, /* 45795 1862 */ {0, 0x0016, 0x8801}, /* 45820 1863 */ {0, 0x0003, 0x8800}, /* 45843 1864 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 45867 1865 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 45891 1866 *//* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ /* 45917 1867 */ {0, 0x0008, 0x8802}, /* 45942 1868 */ {0, 0x0017, 0x8801}, /* 45967 1869 */ {0, 0x0036, 0x8800}, /* 45990 1870 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 46014 1871 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 46038 1872 *//* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ /* 46064 1873 */ {0, 0x0008, 0x8802}, /* 46089 1874 */ {0, 0x0018, 0x8801}, /* 46114 1875 */ {0, 0x00ec, 0x8800}, /* 46137 1876 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 46161 1877 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 46185 1878 *//* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ /* 46211 1879 */ {0, 0x0008, 0x8802}, /* 46236 1880 */ {0, 0x001a, 0x8801}, /* 46261 1881 */ {0, 0x0094, 0x8800}, /* 46284 1882 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 46308 1883 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 46332 1884 *//* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ /* 46358 1885 */ {0, 0x0008, 0x8802}, /* 46383 1886 */ {0, 0x001b, 0x8801}, /* 46408 1887 */ {0, 0x0000, 0x8800}, /* 46431 1888 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 46455 1889 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 46479 1890 *//* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ /* 46505 1891 */ {0, 0x0008, 0x8802}, /* 46530 1892 */ {0, 0x0027, 0x8801}, /* 46555 1893 */ {0, 0x00a2, 0x8800}, /* 46578 1894 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 46602 1895 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 46626 1896 *//* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ /* 46652 1897 */ {0, 0x0008, 0x8802}, /* 46677 1898 */ {0, 0x0028, 0x8801}, /* 46702 1899 */ {0, 0x0040, 0x8800}, /* 46725 1900 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 46749 1901 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 46773 1902 *//* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ /* 46799 1903 */ {0, 0x0008, 0x8802}, /* 46824 1904 */ {0, 0x002a, 0x8801}, /* 46849 1905 */ {0, 0x0084, 0x8800}, /* 46872 1906 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 46896 1907 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 46920 1908 *//* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ /* 46946 1909 */ {0, 0x0008, 0x8802}, /* 46971 1910 */ {0, 0x002b, 0x8801}, /* 46996 1911 */ {0, 0x00a8, 0x8800}, /* 47019 1912 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 47043 1913 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 47067 1914 *//* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ /* 47093 1915 */ {0, 0x0008, 0x8802}, /* 47118 1916 */ {0, 0x002c, 0x8801}, /* 47143 1917 */ {0, 0x00fe, 0x8800}, /* 47166 1918 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 47190 1919 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 47214 1920 *//* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ /* 47240 1921 */ {0, 0x0008, 0x8802}, /* 47265 1922 */ {0, 0x002d, 0x8801}, /* 47290 1923 */ {0, 0x0003, 0x8800}, /* 47313 1924 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 47337 1925 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 47361 1926 *//* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ /* 47387 1927 */ {0, 0x0008, 0x8802}, /* 47412 1928 */ {0, 0x0038, 0x8801}, /* 47437 1929 */ {0, 0x0083, 0x8800}, /* 47460 1930 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 47484 1931 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 47508 1932 *//* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ /* 47534 1933 */ {0, 0x0008, 0x8802}, /* 47559 1934 */ {0, 0x0033, 0x8801}, /* 47584 1935 */ {0, 0x0081, 0x8800}, /* 47607 1936 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 47631 1937 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 47655 1938 *//* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ /* 47681 1939 */ {0, 0x0008, 0x8802}, /* 47706 1940 */ {0, 0x0034, 0x8801}, /* 47731 1941 */ {0, 0x004a, 0x8800}, /* 47754 1942 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 47778 1943 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 47802 1944 *//* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ /* 47828 1945 */ {0, 0x0008, 0x8802}, /* 47853 1946 */ {0, 0x0039, 0x8801}, /* 47878 1947 */ {0, 0x0000, 0x8800}, /* 47901 1948 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 47925 1949 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 47949 1950 *//* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ /* 47975 1951 */ {0, 0x0008, 0x8802}, /* 48000 1952 */ {0, 0x0010, 0x8801}, /* 48025 1953 */ {0, 0x00a8, 0x8800}, /* 48048 1954 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 48072 1955 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 48096 1956 *//* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ /* 48122 1957 */ {0, 0x0008, 0x8802}, /* 48147 1958 */ {0, 0x0006, 0x8801}, /* 48172 1959 */ {0, 0x0058, 0x8800}, /* 48195 1960 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 48219 1961 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 48243 1962 *//* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ /* 48269 1963 */ {0, 0x0008, 0x8802}, /* 48294 1964 */ {0, 0x0000, 0x8801}, /* 48319 1965 */ {0, 0x0004, 0x8800}, /* 48342 1966 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 48366 1967 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 48390 1968 *//* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ /* 48416 1969 */ {0, 0x0008, 0x8802}, /* 48441 1970 */ {0, 0x0040, 0x8801}, /* 48466 1971 */ {0, 0x0080, 0x8800}, /* 48489 1972 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 48513 1973 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 48537 1974 *//* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ /* 48563 1975 */ {0, 0x0008, 0x8802}, /* 48588 1976 */ {0, 0x0041, 0x8801}, /* 48613 1977 */ {0, 0x000c, 0x8800}, /* 48636 1978 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 48660 1979 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 48684 1980 *//* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ /* 48710 1981 */ {0, 0x0008, 0x8802}, /* 48735 1982 */ {0, 0x0042, 0x8801}, /* 48760 1983 */ {0, 0x000c, 0x8800}, /* 48783 1984 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 48807 1985 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 48831 1986 *//* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ /* 48857 1987 */ {0, 0x0008, 0x8802}, /* 48882 1988 */ {0, 0x0043, 0x8801}, /* 48907 1989 */ {0, 0x0028, 0x8800}, /* 48930 1990 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 48954 1991 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 48978 1992 *//* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ /* 49004 1993 */ {0, 0x0008, 0x8802}, /* 49029 1994 */ {0, 0x0044, 0x8801}, /* 49054 1995 */ {0, 0x0080, 0x8800}, /* 49077 1996 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 49101 1997 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 49125 1998 *//* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ /* 49151 1999 */ {0, 0x0008, 0x8802}, /* 49176 2000 */ {0, 0x0045, 0x8801}, /* 49201 2001 */ {0, 0x0020, 0x8800}, /* 49224 2002 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 49248 2003 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 49272 2004 *//* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ /* 49298 2005 */ {0, 0x0008, 0x8802}, /* 49323 2006 */ {0, 0x0046, 0x8801}, /* 49348 2007 */ {0, 0x0020, 0x8800}, /* 49371 2008 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 49395 2009 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 49419 2010 *//* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ /* 49445 2011 */ {0, 0x0008, 0x8802}, /* 49470 2012 */ {0, 0x0047, 0x8801}, /* 49495 2013 */ {0, 0x0080, 0x8800}, /* 49518 2014 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 49542 2015 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 49566 2016 *//* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ /* 49592 2017 */ {0, 0x0008, 0x8802}, /* 49617 2018 */ {0, 0x0048, 0x8801}, /* 49642 2019 */ {0, 0x004c, 0x8800}, /* 49665 2020 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 49689 2021 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 49713 2022 *//* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ /* 49739 2023 */ {0, 0x0008, 0x8802}, /* 49764 2024 */ {0, 0x0049, 0x8801}, /* 49789 2025 */ {0, 0x0084, 0x8800}, /* 49812 2026 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 49836 2027 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 49860 2028 *//* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ /* 49886 2029 */ {0, 0x0008, 0x8802}, /* 49911 2030 */ {0, 0x004a, 0x8801}, /* 49936 2031 */ {0, 0x0084, 0x8800}, /* 49959 2032 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 49983 2033 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 50007 2034 *//* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ /* 50033 2035 */ {0, 0x0008, 0x8802}, /* 50058 2036 */ {0, 0x004b, 0x8801}, /* 50083 2037 */ {0, 0x0084, 0x8800}, /* 50106 2038 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ // --------------------------------------- /* 50132 2039 */ {0, 0x0012, 0x8700}, // Clock speed 48Mhz/(2+2)/2= 6 Mhz /* 50157 2040 */ {0, 0x0000, 0x8701}, // CKx1 clock delay adj /* 50182 2041 */ {0, 0x0000, 0x8701}, // CKx1 clock delay adj /* 50207 2042 */ {0, 0x0001, 0x870c}, // CKOx2 output // --------------------------------------- /* 50232 2043 */ {0, 0x0080, 0x8600}, // Line memory read counter (L) /* 50257 2044 */ {0, 0x0001, 0x8606}, // reserved /* 50282 2045 */ {0, 0x0064, 0x8607}, // Line memory read counter (H) 0x6480=25,728 /* 50307 2046 */ {0, 0x002a, 0x8601}, // CDSP sharp interpolation mode, line sel for color sep, edge enhance enab /* 50332 2047 */ {0, 0x0000, 0x8602}, // optical black level for user settng = 0 /* 50357 2048 */ {0, 0x0080, 0x8600}, // Line memory read counter (L) /* 50382 2049 */ {0, 0x000a, 0x8603}, // optical black level calc mode: auto; optical black offset = 10 /* 50407 2050 */ {0, 0x00df, 0x865b}, // Horiz offset for valid pixels (L)=0xdf /* 50432 2051 */ {0, 0x0012, 0x865c}, // Vert offset for valid lines (L)=0x12 // The following two lines seem to be the "wrong" resolution. // But perhaps these indicate the actual size of the sensor // rather than the size of the current video mode. /* 50457 2052 */ {0, 0x0058, 0x865d}, // Horiz valid pixels (*4) (L) = 352 /* 50482 2053 */ {0, 0x0048, 0x865e}, // Vert valid lines (*4) (L) = 288 /* 50507 2054 */ {0, 0x0015, 0x8608}, // A11 Coef ... /* 50532 2055 */ {0, 0x0030, 0x8609}, /* 50557 2056 */ {0, 0x00fb, 0x860a}, /* 50582 2057 */ {0, 0x003e, 0x860b}, /* 50607 2058 */ {0, 0x00ce, 0x860c}, /* 50632 2059 */ {0, 0x00f4, 0x860d}, /* 50657 2060 */ {0, 0x00eb, 0x860e}, /* 50682 2061 */ {0, 0x00dc, 0x860f}, /* 50707 2062 */ {0, 0x0039, 0x8610}, /* 50732 2063 */ {0, 0x0001, 0x8611}, // R offset for white balance ... /* 50757 2064 */ {0, 0x0000, 0x8612}, /* 50782 2065 */ {0, 0x0001, 0x8613}, /* 50807 2066 */ {0, 0x0000, 0x8614}, /* 50832 2067 */ {0, 0x005b, 0x8651}, // R gain for white balance ... /* 50857 2068 */ {0, 0x0040, 0x8652}, /* 50882 2069 */ {0, 0x0060, 0x8653}, /* 50907 2070 */ {0, 0x0040, 0x8654}, /* 50932 2071 */ {0, 0x0000, 0x8655}, /* 50957 2072 */ {0, 0x0001, 0x863f}, // Fixed gamma correction enable, USB control, lum filter disable, lum noise clip disable /* 50982 2073 */ {0, 0x00a1, 0x8656}, // Window1 size 256x256, Windows2 size 64x64, gamma look-up disable, new edge enhancement enable /* 51007 2074 */ {0, 0x0018, 0x8657}, // Edge gain high thresh /* 51032 2075 */ {0, 0x0020, 0x8658}, // Edge gain low thresh /* 51057 2076 */ {0, 0x000a, 0x8659}, // Edge bandwidth high threshold /* 51082 2077 */ {0, 0x0005, 0x865a}, // Edge bandwidth low threshold //-------------------------------- /* 51107 2078 */ {0, 0x0030, 0x8112}, // Video drop enable, ISO streaming enable /* 51130 2079 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 51154 2080 *//* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ /* 51180 2081 */ {0, 0xa908, 0x8802}, /* 51205 2082 */ {0, 0x0034, 0x8801}, // SSI reg addr /* 51230 2083 */ {0, 0x00ca, 0x8800}, // SSI data to write /* 51253 2084 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 51277 2085 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 51301 2086 *//* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ /* 51327 2087 */ {0, 0x1f08, 0x8802}, /* 51352 2088 */ {0, 0x0006, 0x8801}, /* 51377 2089 */ {0, 0x0080, 0x8800}, /* 51400 2090 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ //----- Read back coefs we wrote earlier. /* 51424 2091 *//* READ { 0, 0x0000, 0x8608 } -> 0000: 15 */ /* 51448 2092 *//* READ { 0, 0x0000, 0x8609 } -> 0000: 30 */ /* 51472 2093 *//* READ { 0, 0x0000, 0x860a } -> 0000: fb */ /* 51496 2094 *//* READ { 0, 0x0000, 0x860b } -> 0000: 3e */ /* 51520 2095 *//* READ { 0, 0x0000, 0x860c } -> 0000: ce */ /* 51544 2096 *//* READ { 0, 0x0000, 0x860d } -> 0000: f4 */ /* 51568 2097 *//* READ { 0, 0x0000, 0x860e } -> 0000: eb */ /* 51592 2098 *//* READ { 0, 0x0000, 0x860f } -> 0000: dc */ /* 51616 2099 *//* READ { 0, 0x0000, 0x8610 } -> 0000: 39 */ /* 51640 2100 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 51664 2101 *//* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ /* 51690 2102 */ {0, 0xb008, 0x8802}, /* 51715 2103 */ {0, 0x0006, 0x8801}, /* 51740 2104 */ {0, 0x007d, 0x8800}, /* 51763 2105 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ #if 0 /* experimental. dark version. */ {0, 0xba, 0x8705}, /* total pixel clocks per hsync cycle (L) */ {0, 0x00, 0x8706}, /* total pixel clocks per hsync cycle (H in 2:0) */ {0, 0x5a, 0x8707}, /* total pixel clocks per hsync blank period (L) */ #elif 0 /* experimental. factory default. */ {0, 0x8e, 0x8705}, /* total pixel clocks per hsync cycle (L) */ {0, 0x03, 0x8706}, /* total pixel clocks per hsync cycle (H in 2:0) */ {0, 0x5a, 0x8707}, /* total pixel clocks per hsync blank period (L) */ #elif 0 /* experimental. light. */ {0, 0xba, 0x8705}, /* total pixel clocks per hsync cycle (L) */ {0, 0x01, 0x8706}, /* total pixel clocks per hsync cycle (H in 2:0) */ {0, 0x10, 0x8707}, /* total pixel clocks per hsync blank period (L) */ #endif #if 1 // This chunk is seemingly redundant with // earlier commands (A11 Coef...), but if I disable it, // the image appears too dark. Maybe there was some kind of // reset since the earlier commands, so this is necessary again. /* 51789 2106 */ {0, 0x0015, 0x8608}, /* 51814 2107 */ {0, 0x0030, 0x8609}, /* 51839 2108 */ {0, 0xfffb, 0x860a}, /* 51864 2109 */ {0, 0x003e, 0x860b}, /* 51889 2110 */ {0, 0xffce, 0x860c}, /* 51914 2111 */ {0, 0xfff4, 0x860d}, /* 51939 2112 */ {0, 0xffeb, 0x860e}, /* 51964 2113 */ {0, 0xffdc, 0x860f}, /* 51989 2114 */ {0, 0x0039, 0x8610}, /* 52014 2115 */ {0, 0x0018, 0x8657}, #endif /* 52039 2116 */ {0, 0x0000, 0x8508}, // Disable compression. // Previous line was: /* 52039 2116 */ { 0, 0x0021, 0x8508 }, // Enable compression. /* 52064 2117 */ {0, 0x0032, 0x850b}, // compression stuff /* 52089 2118 */ {0, 0x0003, 0x8509}, // compression stuff /* 52114 2119 */ {0, 0x0011, 0x850a}, // compression stuff /* 52139 2120 */ {0, 0x0021, 0x850d}, // compression stuff /* 52164 2121 */ {0, 0x0010, 0x850c}, // compression stuff /* 52189 2122 */ {0, 0x0003, 0x8500}, // *** Video mode: 160x120 /* 52214 2123 */ {0, 0x0001, 0x8501}, // Hardware-dominated snap control /* 52239 2124 */ {0, 0x0061, 0x8656}, // Window1 size 128x128, Windows2 size 128x128, gamma look-up disable, new edge enhancement enable /* 52264 2125 */ {0, 0x0018, 0x8617}, // Window1 start X (*2) /* 52289 2126 */ {0, 0x0008, 0x8618}, // Window1 start Y (*2) /* 52314 2127 */ {0, 0x0061, 0x8656}, // Window1 size 128x128, Windows2 size 128x128, gamma look-up disable, new edge enhancement enable /* 52339 2128 */ {0, 0x0058, 0x8619}, // Window2 start X (*2) /* 52364 2129 */ {0, 0x0008, 0x861a}, // Window2 start Y (*2) /* 52389 2130 */ {0, 0x00ff, 0x8615}, // High lum thresh for white balance /* 52414 2131 */ {0, 0x0000, 0x8616}, // Low lum thresh for white balance /* 52439 2132 */ {0, 0x0012, 0x8700}, // Clock speed 48Mhz/(2+2)/2= 6 Mhz /* 52464 2133 */ {0, 0x0012, 0x8700}, // Clock speed 48Mhz/(2+2)/2= 6 Mhz /* 52487 2134 *//* READ { 0, 0x0000, 0x8656 } -> 0000: 61 */ /* 52513 2135 */ {0, 0x0028, 0x8802}, // 375 Khz SSI clock, SSI r/w sync with VSYNC /* 52536 2136 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 52560 2137 *//* READ { 0, 0x0001, 0x8802 } -> 0000: 28 */ /* 52586 2138 */ {0, 0x1f28, 0x8802}, // 375 Khz SSI clock, SSI r/w sync with VSYNC /* 52611 2139 */ {0, 0x0010, 0x8801}, // SSI reg addr /* 52636 2140 */ {0, 0x003e, 0x8800}, // SSI data to write /* 52659 2141 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 52685 2142 */ {0, 0x0028, 0x8802}, /* 52708 2143 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 52732 2144 *//* READ { 0, 0x0001, 0x8802 } -> 0000: 28 */ /* 52758 2145 */ {0, 0x1f28, 0x8802}, /* 52783 2146 */ {0, 0x0000, 0x8801}, /* 52808 2147 */ {0, 0x001f, 0x8800}, /* 52831 2148 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 52857 2149 */ {0, 0x0001, 0x8602}, // optical black level for user settning = 1 #if 0 // NOTE: Code like this case lets this driver (often) work // in 352x288 resolution, apparently by slowing down the // clock. /* 52464 2133 */ {0, 0x002F, 0x8700}, // Clock speed #else // Original: /* 52882 2150 */ {0, 0x0023, 0x8700}, // Clock speed 48Mhz/(3+2)/4= 2.4 Mhz #endif /* 52907 2151 */ {0, 0x000f, 0x8602}, // optical black level for user settning = 15 /* 52932 2152 */ {0, 0x0028, 0x8802}, /* 52955 2153 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 52979 2154 *//* READ { 0, 0x0001, 0x8802 } -> 0000: 28 */ /* 53005 2155 */ {0, 0x1f28, 0x8802}, /* 53030 2156 */ {0, 0x0010, 0x8801}, /* 53055 2157 */ {0, 0x007b, 0x8800}, /* 53078 2158 *//* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* 53104 2159 */ {0, 0x002f, 0x8651}, // R gain for white balance ... /* 53129 2160 */ {0, 0x0080, 0x8653}, /* 53152 2161 *//* READ { 0, 0x0000, 0x8655 } -> 0000: 00 */ /* 53178 2162 */ {0, 0x0000, 0x8655}, /* 53203 2163 */ {0, 0x0030, 0x8112}, // Video drop enable, ISO streaming enable /* 53228 2164 */ {0, 0x0020, 0x8112}, // Video drop enable, ISO streaming disable /* 53252 2165 *//* UNKNOWN DIRECTION (URB_FUNCTION_SELECT_INTERFACE: (ALT=0) ) */ {0, 0, 0} }; /* * Data to initialize the camera using the internal CCD */ static __u16 spca508_open_data[][3] = { /* line bmRequest,value,index */ {0, 0, 0} }; /* * Initialization data for Intel EasyPC Camera CS110 */ static __u16 spca508cs110_init_data[][3] = { {0, 0x0000, 0x870b}, /* Reset CTL3 */ {0, 0x0003, 0x8111}, /* Soft Reset compression, memory, TG & CDSP */ {0, 0x0000, 0x8111}, /* Normal operation on reset */ {0, 0x0090, 0x8110}, /* External Clock 2x & Synchronous Serial Interface Output */ {0, 0x0020, 0x8112}, /* Video Drop packet enable */ {0, 0x0000, 0x8114}, /* Software GPIO output data */ {0, 0x0001, 0x8114}, {0, 0x0001, 0x8114}, {0, 0x0001, 0x8114}, {0, 0x0003, 0x8114}, /* Initial sequence Synchronous Serial Interface */ {0, 0x000f, 0x8402}, /* Memory bank Address */ {0, 0x0000, 0x8403}, /* Memory bank Address */ {0, 0x00ba, 0x8804}, /* SSI Slave address */ {0, 0x0010, 0x8802}, /* 93.75kHz SSI Clock Two DataByte */ {0, 0x0010, 0x8802}, /* 93.75kHz SSI Clock two DataByte */ {0, 0x0001, 0x8801}, {0, 0x000a, 0x8805}, /* a - NWG: Dunno what this is about */ {0, 0x0000, 0x8800}, {0, 0x0010, 0x8802}, {0, 0x0002, 0x8801}, {0, 0x0000, 0x8805}, {0, 0x0000, 0x8800}, {0, 0x0010, 0x8802}, {0, 0x0003, 0x8801}, {0, 0x0027, 0x8805}, {0, 0x0001, 0x8800}, {0, 0x0010, 0x8802}, {0, 0x0004, 0x8801}, {0, 0x0065, 0x8805}, {0, 0x0001, 0x8800}, {0, 0x0010, 0x8802}, {0, 0x0005, 0x8801}, {0, 0x0003, 0x8805}, {0, 0x0000, 0x8800}, {0, 0x0010, 0x8802}, {0, 0x0006, 0x8801}, {0, 0x001c, 0x8805}, {0, 0x0000, 0x8800}, {0, 0x0010, 0x8802}, {0, 0x0007, 0x8801}, {0, 0x002a, 0x8805}, {0, 0x0000, 0x8800}, {0, 0x0010, 0x8802}, {0, 0x0002, 0x8704}, /* External input CKIx1 */ {0, 0x0001, 0x8606}, /* 1 Line memory Read Counter (H) Result: (d)410 */ {0, 0x009a, 0x8600}, /* Line memory Read Counter (L) */ {0, 0x0001, 0x865b}, /* 1 Horizontal Offset for Valid Pixel(L) */ {0, 0x0003, 0x865c}, /* 3 Vertical Offset for Valid Lines(L) */ {0, 0x0058, 0x865d}, /* 58 Horizontal Valid Pixel Window(L) */ {0, 0x0006, 0x8660}, /* Nibble data + input order */ {0, 0x000a, 0x8602}, /* Optical black level set to 0x0a */ /* 1945 */ {0, 0x0000, 0x8603}, /* Optical black level Offset */ /* 1962 */// { 0, 0x0000, 0x8611 }, /* 0 R Offset for white Balance */ /* 1963 */// { 0, 0x0000, 0x8612 }, /* 1 Gr Offset for white Balance */ /* 1964 */// { 0, 0x0000, 0x8613 }, /* 1f B Offset for white Balance */ /* 1965 */// { 0, 0x0000, 0x8614 }, /* f0 Gb Offset for white Balance */ {0, 0x0040, 0x8651}, /* 2b BLUE gain for white balance good at all 60 */ {0, 0x0030, 0x8652}, /* 41 Gr Gain for white Balance (L) */ {0, 0x0035, 0x8653}, /* 26 RED gain for white balance */ {0, 0x0035, 0x8654}, /* 40Gb Gain for white Balance (L) */ {0, 0x0041, 0x863f}, /* Fixed Gamma correction enabled (makes colours look better) */ /* 2422 */ {0, 0x0000, 0x8655}, /* High bits for white balance*****brightness control*** */ {0, 0, 0} }; static __u16 spca508_sightcam_init_data[][3] = { /* This line seems to setup the frame/canvas */ /*368 */ {0, 0x000f, 0x8402}, /* Theese 6 lines are needed to startup the webcam */ /*398 */ {0, 0x0090, 0x8110}, /*399 */ {0, 0x0001, 0x8114}, /*400 */ {0, 0x0001, 0x8114}, /*401 */ {0, 0x0001, 0x8114}, /*402 */ {0, 0x0003, 0x8114}, /*403 */ {0, 0x0080, 0x8804}, /* This part seems to make the pictures darker? (autobrightness?) */ /*436 */ {0, 0x0001, 0x8801}, /*437 */ {0, 0x0004, 0x8800}, /*439 */ {0, 0x0003, 0x8801}, /*440 */ {0, 0x00e0, 0x8800}, /*442 */ {0, 0x0004, 0x8801}, /*443 */ {0, 0x00b4, 0x8800}, /*445 */ {0, 0x0005, 0x8801}, /*446 */ {0, 0x0000, 0x8800}, /*448 */ {0, 0x0006, 0x8801}, /*449 */ {0, 0x00e0, 0x8800}, /*451 */ {0, 0x0007, 0x8801}, /*452 */ {0, 0x000c, 0x8800}, /* This section is just needed, it probably * does something like the previous section, * but the cam won't start if it's not included. */ /*484 */ {0, 0x0014, 0x8801}, /*485 */ {0, 0x0008, 0x8800}, /*487 */ {0, 0x0015, 0x8801}, /*488 */ {0, 0x0067, 0x8800}, /*490 */ {0, 0x0016, 0x8801}, /*491 */ {0, 0x0000, 0x8800}, /*493 */ {0, 0x0017, 0x8801}, /*494 */ {0, 0x0020, 0x8800}, /*496 */ {0, 0x0018, 0x8801}, /*497 */ {0, 0x0044, 0x8800}, /* Makes the picture darker - and the * cam won't start if not included */ /*505 */ {0, 0x001e, 0x8801}, /*506 */ {0, 0x00ea, 0x8800}, /*508 */ {0, 0x001f, 0x8801}, /*509 */ {0, 0x0001, 0x8800}, /*511 */ {0, 0x0003, 0x8801}, /*512 */ {0, 0x00e0, 0x8800}, /* seems to place the colors ontop of each other #1 */ /*517 */ {0, 0x0006, 0x8704}, /*518 */ {0, 0x0001, 0x870c}, /*519 */ {0, 0x0016, 0x8600}, /*520 */ {0, 0x0002, 0x8606}, /* if not included the pictures becomes _very_ dark */ /*521 */ {0, 0x0064, 0x8607}, /*522 */ {0, 0x003a, 0x8601}, /*523 */ {0, 0x0000, 0x8602}, /* seems to place the colors ontop of each other #2 */ /*524 */ {0, 0x0016, 0x8600}, /*525 */ {0, 0x0018, 0x8617}, /*526 */ {0, 0x0008, 0x8618}, /*527 */ {0, 0x00a1, 0x8656}, /* webcam won't start if not included */ /*528 */ {0, 0x0007, 0x865b}, /*529 */ {0, 0x0001, 0x865c}, /*530 */ {0, 0x0058, 0x865d}, /*531 */ {0, 0x0048, 0x865e}, /* adjusts the colors */ /*541 */ {0, 0x0049, 0x8651}, /*542 */ {0, 0x0040, 0x8652}, /*543 */ {0, 0x004c, 0x8653}, /*544 */ {0, 0x0040, 0x8654}, {0, 0, 0} }; static __u16 spca508_sightcam2_init_data[][3] = { #if 1 /* 35 */ {0, 0x0020, 0x8112}, /* 36 */ {0, 0x000f, 0x8402}, /* 37 */ {0, 0x0000, 0x8403}, /* 38 */ {0, 0x0008, 0x8201}, /* 39 */ {0, 0x0008, 0x8200}, /* 40 */ {0, 0x0001, 0x8200}, /* 43 */ {0, 0x0009, 0x8201}, /* 44 */ {0, 0x0008, 0x8200}, /* 45 */ {0, 0x0001, 0x8200}, /* 48 */ {0, 0x000a, 0x8201}, /* 49 */ {0, 0x0008, 0x8200}, /* 50 */ {0, 0x0001, 0x8200}, /* 53 */ {0, 0x000b, 0x8201}, /* 54 */ {0, 0x0008, 0x8200}, /* 55 */ {0, 0x0001, 0x8200}, /* 58 */ {0, 0x000c, 0x8201}, /* 59 */ {0, 0x0008, 0x8200}, /* 60 */ {0, 0x0001, 0x8200}, /* 63 */ {0, 0x000d, 0x8201}, /* 64 */ {0, 0x0008, 0x8200}, /* 65 */ {0, 0x0001, 0x8200}, /* 68 */ {0, 0x000e, 0x8201}, /* 69 */ {0, 0x0008, 0x8200}, /* 70 */ {0, 0x0001, 0x8200}, /* 73 */ {0, 0x0007, 0x8201}, /* 74 */ {0, 0x0008, 0x8200}, /* 75 */ {0, 0x0001, 0x8200}, /* 78 */ {0, 0x000f, 0x8201}, /* 79 */ {0, 0x0008, 0x8200}, /* 80 */ {0, 0x0001, 0x8200}, /* 84 */ {0, 0x0018, 0x8660}, /* 85 */ {0, 0x0010, 0x8201}, /* 86 */ {0, 0x0008, 0x8200}, /* 87 */ {0, 0x0001, 0x8200}, /* 90 */ {0, 0x0011, 0x8201}, /* 91 */ {0, 0x0008, 0x8200}, /* 92 */ {0, 0x0001, 0x8200}, /* 95 */ {0, 0x0000, 0x86b0}, /* 96 */ {0, 0x0034, 0x86b1}, /* 97 */ {0, 0x0000, 0x86b2}, /* 98 */ {0, 0x0049, 0x86b3}, /* 99 */ {0, 0x0000, 0x86b4}, /* 100 */ {0, 0x0000, 0x86b4}, /* 101 */ {0, 0x0012, 0x8201}, /* 102 */ {0, 0x0008, 0x8200}, /* 103 */ {0, 0x0001, 0x8200}, /* 106 */ {0, 0x0013, 0x8201}, /* 107 */ {0, 0x0008, 0x8200}, /* 108 */ {0, 0x0001, 0x8200}, /* 111 */ {0, 0x0001, 0x86b0}, /* 112 */ {0, 0x00aa, 0x86b1}, /* 113 */ {0, 0x0000, 0x86b2}, /* 114 */ {0, 0x00e4, 0x86b3}, /* 115 */ {0, 0x0000, 0x86b4}, /* 116 */ {0, 0x0000, 0x86b4}, /* 118 */ {0, 0x0018, 0x8660}, /* 119 */ {0, 0x0090, 0x8110}, /* 120 */ {0, 0x0001, 0x8114}, /* 121 */ {0, 0x0001, 0x8114}, /* 122 */ {0, 0x0001, 0x8114}, /* 123 */ {0, 0x0003, 0x8114}, /* 124 */ {0, 0x0080, 0x8804}, /* 157 */ {0, 0x0003, 0x8801}, /* 158 */ {0, 0x0012, 0x8800}, /* 160 */ {0, 0x0004, 0x8801}, /* 161 */ {0, 0x0005, 0x8800}, /* 163 */ {0, 0x0005, 0x8801}, /* 164 */ {0, 0x0000, 0x8800}, /* 166 */ {0, 0x0006, 0x8801}, /* 167 */ {0, 0x0000, 0x8800}, /* 169 */ {0, 0x0007, 0x8801}, /* 170 */ {0, 0x0000, 0x8800}, /* 172 */ {0, 0x0008, 0x8801}, /* 173 */ {0, 0x0005, 0x8800}, /* 175 */ {0, 0x000a, 0x8700}, /* 176 */ {0, 0x000e, 0x8801}, /* 177 */ {0, 0x0004, 0x8800}, /* 179 */ {0, 0x0005, 0x8801}, /* 180 */ {0, 0x0047, 0x8800}, /* 182 */ {0, 0x0006, 0x8801}, /* 183 */ {0, 0x0000, 0x8800}, /* 185 */ {0, 0x0007, 0x8801}, /* 186 */ {0, 0x00c0, 0x8800}, /* 188 */ {0, 0x0008, 0x8801}, /* 189 */ {0, 0x0003, 0x8800}, /* 191 */ {0, 0x0013, 0x8801}, /* 192 */ {0, 0x0001, 0x8800}, /* 194 */ {0, 0x0009, 0x8801}, /* 195 */ {0, 0x0000, 0x8800}, /* 197 */ {0, 0x000a, 0x8801}, /* 198 */ {0, 0x0000, 0x8800}, /* 200 */ {0, 0x000b, 0x8801}, /* 201 */ {0, 0x0000, 0x8800}, /* 203 */ {0, 0x000c, 0x8801}, /* 204 */ {0, 0x0000, 0x8800}, /* 206 */ {0, 0x000e, 0x8801}, /* 207 */ {0, 0x0004, 0x8800}, /* 209 */ {0, 0x000f, 0x8801}, /* 210 */ {0, 0x0000, 0x8800}, /* 212 */ {0, 0x0010, 0x8801}, /* 213 */ {0, 0x0006, 0x8800}, /* 215 */ {0, 0x0011, 0x8801}, /* 216 */ {0, 0x0006, 0x8800}, /* 218 */ {0, 0x0012, 0x8801}, /* 219 */ {0, 0x0000, 0x8800}, /* 221 */ {0, 0x0013, 0x8801}, /* 222 */ {0, 0x0001, 0x8800}, /* 224 */ {0, 0x000a, 0x8700}, /* 225 */ {0, 0x0000, 0x8702}, /* 226 */ {0, 0x0000, 0x8703}, /* 227 */ {0, 0x00c2, 0x8704}, /* 228 */ {0, 0x0001, 0x870c}, /* 229 */ {0, 0x0044, 0x8600}, /* 230 */ {0, 0x0002, 0x8606}, /* 231 */ {0, 0x0064, 0x8607}, /* 232 */ {0, 0x003a, 0x8601}, /* 233 */ {0, 0x0008, 0x8602}, /* 234 */ {0, 0x0044, 0x8600}, /* 235 */ {0, 0x0018, 0x8617}, /* 236 */ {0, 0x0008, 0x8618}, /* 237 */ {0, 0x00a1, 0x8656}, /* 238 */ {0, 0x0004, 0x865b}, /* 239 */ {0, 0x0002, 0x865c}, /* 240 */ {0, 0x0058, 0x865d}, /* 241 */ {0, 0x0048, 0x865e}, /* 242 */ {0, 0x0012, 0x8608}, /* 243 */ {0, 0x002c, 0x8609}, /* 244 */ {0, 0x0002, 0x860a}, /* 245 */ {0, 0x002c, 0x860b}, /* 246 */ {0, 0x00db, 0x860c}, /* 247 */ {0, 0x00f9, 0x860d}, /* 248 */ {0, 0x00f1, 0x860e}, /* 249 */ {0, 0x00e3, 0x860f}, /* 250 */ {0, 0x002c, 0x8610}, /* 251 */ {0, 0x006c, 0x8651}, /* 252 */ {0, 0x0041, 0x8652}, /* 253 */ {0, 0x0059, 0x8653}, /* 254 */ {0, 0x0040, 0x8654}, /* 255 */ {0, 0x00fa, 0x8611}, /* 256 */ {0, 0x00ff, 0x8612}, /* 257 */ {0, 0x00f8, 0x8613}, /* 258 */ {0, 0x0000, 0x8614}, /* 259 */ {0, 0x0001, 0x863f}, /* 260 */ {0, 0x0000, 0x8640}, /* 261 */ {0, 0x0026, 0x8641}, /* 262 */ {0, 0x0045, 0x8642}, /* 263 */ {0, 0x0060, 0x8643}, /* 264 */ {0, 0x0075, 0x8644}, /* 265 */ {0, 0x0088, 0x8645}, /* 266 */ {0, 0x009b, 0x8646}, /* 267 */ {0, 0x00b0, 0x8647}, /* 268 */ {0, 0x00c5, 0x8648}, /* 269 */ {0, 0x00d2, 0x8649}, /* 270 */ {0, 0x00dc, 0x864a}, /* 271 */ {0, 0x00e5, 0x864b}, /* 272 */ {0, 0x00eb, 0x864c}, /* 273 */ {0, 0x00f0, 0x864d}, /* 274 */ {0, 0x00f6, 0x864e}, /* 275 */ {0, 0x00fa, 0x864f}, /* 276 */ {0, 0x00ff, 0x8650}, /* 277 */ {0, 0x0060, 0x8657}, /* 278 */ {0, 0x0010, 0x8658}, /* 279 */ {0, 0x0018, 0x8659}, /* 280 */ {0, 0x0005, 0x865a}, /* 281 */ {0, 0x0018, 0x8660}, /* 282 */ {0, 0x0003, 0x8509}, /* 283 */ {0, 0x0011, 0x850a}, /* 284 */ {0, 0x0032, 0x850b}, /* 285 */ {0, 0x0010, 0x850c}, /* 286 */ {0, 0x0021, 0x850d}, /* 287 */ {0, 0x0001, 0x8500}, /* 288 */ {0, 0x0000, 0x8508}, /* 289 */ {0, 0x0012, 0x8608}, /* 290 */ {0, 0x002c, 0x8609}, /* 291 */ {0, 0x0002, 0x860a}, /* 292 */ {0, 0x0039, 0x860b}, /* 293 */ {0, 0x00d0, 0x860c}, /* 294 */ {0, 0x00f7, 0x860d}, /* 295 */ {0, 0x00ed, 0x860e}, /* 296 */ {0, 0x00db, 0x860f}, /* 297 */ {0, 0x0039, 0x8610}, /* 298 */ {0, 0x0012, 0x8657}, /* 299 */ {0, 0x000c, 0x8619}, /* 300 */ {0, 0x0004, 0x861a}, /* 301 */ {0, 0x00a1, 0x8656}, /* 302 */ {0, 0x00c8, 0x8615}, /* 303 */ {0, 0x0032, 0x8616}, /* 306 */ {0, 0x0030, 0x8112}, /* 313 */ {0, 0x0020, 0x8112}, /* 314 */ {0, 0x0020, 0x8112}, /* 315 */ {0, 0x000f, 0x8402}, /* 316 */ {0, 0x0000, 0x8403}, /* 317 */ {0, 0x0090, 0x8110}, /* 318 */ {0, 0x0001, 0x8114}, /* 319 */ {0, 0x0001, 0x8114}, /* 320 */ {0, 0x0001, 0x8114}, /* 321 */ {0, 0x0003, 0x8114}, /* 322 */ {0, 0x0080, 0x8804}, /* 355 */ {0, 0x0003, 0x8801}, /* 356 */ {0, 0x0012, 0x8800}, /* 358 */ {0, 0x0004, 0x8801}, /* 359 */ {0, 0x0005, 0x8800}, /* 361 */ {0, 0x0005, 0x8801}, /* 362 */ {0, 0x0047, 0x8800}, /* 364 */ {0, 0x0006, 0x8801}, /* 365 */ {0, 0x0000, 0x8800}, /* 367 */ {0, 0x0007, 0x8801}, /* 368 */ {0, 0x00c0, 0x8800}, /* 370 */ {0, 0x0008, 0x8801}, /* 371 */ {0, 0x0003, 0x8800}, /* 373 */ {0, 0x000a, 0x8700}, /* 374 */ {0, 0x000e, 0x8801}, /* 375 */ {0, 0x0004, 0x8800}, /* 377 */ {0, 0x0005, 0x8801}, /* 378 */ {0, 0x0047, 0x8800}, /* 380 */ {0, 0x0006, 0x8801}, /* 381 */ {0, 0x0000, 0x8800}, /* 383 */ {0, 0x0007, 0x8801}, /* 384 */ {0, 0x00c0, 0x8800}, /* 386 */ {0, 0x0008, 0x8801}, /* 387 */ {0, 0x0003, 0x8800}, /* 389 */ {0, 0x0013, 0x8801}, /* 390 */ {0, 0x0001, 0x8800}, /* 392 */ {0, 0x0009, 0x8801}, /* 393 */ {0, 0x0000, 0x8800}, /* 395 */ {0, 0x000a, 0x8801}, /* 396 */ {0, 0x0000, 0x8800}, /* 398 */ {0, 0x000b, 0x8801}, /* 399 */ {0, 0x0000, 0x8800}, /* 401 */ {0, 0x000c, 0x8801}, /* 402 */ {0, 0x0000, 0x8800}, /* 404 */ {0, 0x000e, 0x8801}, /* 405 */ {0, 0x0004, 0x8800}, /* 407 */ {0, 0x000f, 0x8801}, /* 408 */ {0, 0x0000, 0x8800}, /* 410 */ {0, 0x0010, 0x8801}, /* 411 */ {0, 0x0006, 0x8800}, /* 413 */ {0, 0x0011, 0x8801}, /* 414 */ {0, 0x0006, 0x8800}, /* 416 */ {0, 0x0012, 0x8801}, /* 417 */ {0, 0x0000, 0x8800}, /* 419 */ {0, 0x0013, 0x8801}, /* 420 */ {0, 0x0001, 0x8800}, /* 422 */ {0, 0x000a, 0x8700}, /* 423 */ {0, 0x0000, 0x8702}, /* 424 */ {0, 0x0000, 0x8703}, /* 425 */ {0, 0x00c2, 0x8704}, /* 426 */ {0, 0x0001, 0x870c}, /* 427 */ {0, 0x0044, 0x8600}, /* 428 */ {0, 0x0002, 0x8606}, /* 429 */ {0, 0x0064, 0x8607}, /* 430 */ {0, 0x003a, 0x8601}, /* 431 */ {0, 0x0008, 0x8602}, /* 432 */ {0, 0x0044, 0x8600}, /* 433 */ {0, 0x0018, 0x8617}, /* 434 */ {0, 0x0008, 0x8618}, /* 435 */ {0, 0x00a1, 0x8656}, /* 436 */ {0, 0x0004, 0x865b}, /* 437 */ {0, 0x0002, 0x865c}, /* 438 */ {0, 0x0058, 0x865d}, /* 439 */ {0, 0x0048, 0x865e}, /* 440 */ {0, 0x0012, 0x8608}, /* 441 */ {0, 0x002c, 0x8609}, /* 442 */ {0, 0x0002, 0x860a}, /* 443 */ {0, 0x002c, 0x860b}, /* 444 */ {0, 0x00db, 0x860c}, /* 445 */ {0, 0x00f9, 0x860d}, /* 446 */ {0, 0x00f1, 0x860e}, /* 447 */ {0, 0x00e3, 0x860f}, /* 448 */ {0, 0x002c, 0x8610}, /* 449 */ {0, 0x006c, 0x8651}, /* 450 */ {0, 0x0041, 0x8652}, /* 451 */ {0, 0x0059, 0x8653}, /* 452 */ {0, 0x0040, 0x8654}, /* 453 */ {0, 0x00fa, 0x8611}, /* 454 */ {0, 0x00ff, 0x8612}, /* 455 */ {0, 0x00f8, 0x8613}, /* 456 */ {0, 0x0000, 0x8614}, /* 457 */ {0, 0x0001, 0x863f}, /* 458 */ {0, 0x0000, 0x8640}, /* 459 */ {0, 0x0026, 0x8641}, /* 460 */ {0, 0x0045, 0x8642}, /* 461 */ {0, 0x0060, 0x8643}, /* 462 */ {0, 0x0075, 0x8644}, /* 463 */ {0, 0x0088, 0x8645}, /* 464 */ {0, 0x009b, 0x8646}, /* 465 */ {0, 0x00b0, 0x8647}, /* 466 */ {0, 0x00c5, 0x8648}, /* 467 */ {0, 0x00d2, 0x8649}, /* 468 */ {0, 0x00dc, 0x864a}, /* 469 */ {0, 0x00e5, 0x864b}, /* 470 */ {0, 0x00eb, 0x864c}, /* 471 */ {0, 0x00f0, 0x864d}, /* 472 */ {0, 0x00f6, 0x864e}, /* 473 */ {0, 0x00fa, 0x864f}, /* 474 */ {0, 0x00ff, 0x8650}, /* 475 */ {0, 0x0060, 0x8657}, /* 476 */ {0, 0x0010, 0x8658}, /* 477 */ {0, 0x0018, 0x8659}, /* 478 */ {0, 0x0005, 0x865a}, /* 479 */ {0, 0x0018, 0x8660}, /* 480 */ {0, 0x0003, 0x8509}, /* 481 */ {0, 0x0011, 0x850a}, /* 482 */ {0, 0x0032, 0x850b}, /* 483 */ {0, 0x0010, 0x850c}, /* 484 */ {0, 0x0021, 0x850d}, /* 485 */ {0, 0x0001, 0x8500}, /* 486 */ {0, 0x0000, 0x8508}, /* 487 */ {0, 0x0012, 0x8608}, /* 488 */ {0, 0x002c, 0x8609}, /* 489 */ {0, 0x0002, 0x860a}, /* 490 */ {0, 0x0039, 0x860b}, /* 491 */ {0, 0x00d0, 0x860c}, /* 492 */ {0, 0x00f7, 0x860d}, /* 493 */ {0, 0x00ed, 0x860e}, /* 494 */ {0, 0x00db, 0x860f}, /* 495 */ {0, 0x0039, 0x8610}, /* 496 */ {0, 0x0012, 0x8657}, /* 497 */ {0, 0x0064, 0x8619}, /* This line starts it all, it is not needed here */ /* since it has been build into the driver*/ /*590 */ {0, 0x0030, 0x8112}, #endif {0, 0, 0} }; /* * Initialization data for Creative Webcam Vista */ static __u16 spca508_vista_init_data[][3] = { {0, 0x0008, 0x8200}, /* Clear register */ {0, 0x0000, 0x870b}, /* Reset CTL3 */ {0, 0x0020, 0x8112}, /* Video Drop packet enable */ {0, 0x0003, 0x8111}, /* Soft Reset compression, memory, TG & CDSP */ {0, 0x0000, 0x8110}, /* Disable everything */ {0, 0x0000, 0x8114}, /* Software GPIO output data */ {0, 0x0000, 0x8114}, {0, 0x0003, 0x8111}, {0, 0x0000, 0x8111}, {0, 0x0090, 0x8110}, /* Enable: SSI output, External 2X clock output */ {0, 0x0020, 0x8112}, {0, 0x0000, 0x8114}, {0, 0x0001, 0x8114}, {0, 0x0001, 0x8114}, {0, 0x0001, 0x8114}, {0, 0x0003, 0x8114}, {0, 0x000f, 0x8402}, /* Memory bank Address */ {0, 0x0000, 0x8403}, /* Memory bank Address */ {0, 0x00ba, 0x8804}, /* SSI Slave address */ {0, 0x0010, 0x8802}, /* 93.75kHz SSI Clock Two DataByte */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 10 */ {0, 0x0010, 0x8802}, /* Will write 2 bytes (DATA1+DATA2) */ {0, 0x0020, 0x8801}, /* Register address for SSI read/write */ {0, 0x0044, 0x8805}, /* DATA2 */ {0, 0x0004, 0x8800}, /* DATA1 -> write triggered */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 10 */ {0, 0x0010, 0x8802}, {0, 0x0009, 0x8801}, {0, 0x0042, 0x8805}, {0, 0x0001, 0x8800}, /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 10 */ {0, 0x0010, 0x8802}, {0, 0x003c, 0x8801}, {0, 0x0001, 0x8805}, {0, 0x0000, 0x8800}, /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 10 */ {0, 0x0010, 0x8802}, {0, 0x0001, 0x8801}, {0, 0x000a, 0x8805}, {0, 0x0000, 0x8800}, /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 10 */ {0, 0x0010, 0x8802}, {0, 0x0002, 0x8801}, {0, 0x0000, 0x8805}, {0, 0x0000, 0x8800}, /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 10 */ {0, 0x0010, 0x8802}, {0, 0x0003, 0x8801}, {0, 0x0027, 0x8805}, {0, 0x0001, 0x8800}, /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 10 */ {0, 0x0010, 0x8802}, {0, 0x0004, 0x8801}, {0, 0x0065, 0x8805}, {0, 0x0001, 0x8800}, /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 10 */ {0, 0x0010, 0x8802}, {0, 0x0005, 0x8801}, {0, 0x0003, 0x8805}, {0, 0x0000, 0x8800}, /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 10 */ {0, 0x0010, 0x8802}, {0, 0x0006, 0x8801}, {0, 0x001c, 0x8805}, {0, 0x0000, 0x8800}, /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 10 */ {0, 0x0010, 0x8802}, {0, 0x0007, 0x8801}, {0, 0x002a, 0x8805}, {0, 0x0000, 0x8800}, /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 10 */ {0, 0x0010, 0x8802}, {0, 0x000e, 0x8801}, {0, 0x0000, 0x8805}, {0, 0x0000, 0x8800}, /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 10 */ {0, 0x0010, 0x8802}, {0, 0x0028, 0x8801}, {0, 0x002e, 0x8805}, {0, 0x0000, 0x8800}, /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 10 */ {0, 0x0010, 0x8802}, {0, 0x0039, 0x8801}, {0, 0x0013, 0x8805}, {0, 0x0000, 0x8800}, /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 10 */ {0, 0x0010, 0x8802}, {0, 0x003b, 0x8801}, {0, 0x000c, 0x8805}, {0, 0x0000, 0x8800}, /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 10 */ {0, 0x0010, 0x8802}, {0, 0x0035, 0x8801}, {0, 0x0028, 0x8805}, {0, 0x0000, 0x8800}, /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 10 */ {0, 0x0010, 0x8802}, {0, 0x0009, 0x8801}, {0, 0x0042, 0x8805}, {0, 0x0001, 0x8800}, /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ {0, 0x0050, 0x8703}, {0, 0x0002, 0x8704}, /* External input CKIx1 */ {0, 0x0001, 0x870C}, /* Select CKOx2 output */ {0, 0x009A, 0x8600}, /* Line memory Read Counter (L) */ {0, 0x0001, 0x8606}, /* 1 Line memory Read Counter (H) Result: (d)410 */ {0, 0x0023, 0x8601}, {0, 0x0010, 0x8602}, {0, 0x000A, 0x8603}, {0, 0x009A, 0x8600}, {0, 0x0001, 0x865B}, /* 1 Horizontal Offset for Valid Pixel(L) */ {0, 0x0003, 0x865C}, /* Vertical offset for valid lines (L) */ {0, 0x0058, 0x865D}, /* Horizontal valid pixels window (L) */ {0, 0x0048, 0x865E}, /* Vertical valid lines window (L) */ {0, 0x0000, 0x865F}, {0, 0x0006, 0x8660}, /* Enable nibble data input, select nibble input order */ {0, 0x0013, 0x8608}, /* A11 Coeficients for color correction */ {0, 0x0028, 0x8609}, /* Note: these values are confirmed at the end of array */ {0, 0x0005, 0x860A}, /* ... */ {0, 0x0025, 0x860B}, {0, 0x00E1, 0x860C}, {0, 0x00FA, 0x860D}, {0, 0x00F4, 0x860E}, {0, 0x00E8, 0x860F}, {0, 0x0025, 0x8610}, /* A33 Coef. */ {0, 0x00FC, 0x8611}, /* White balance offset: R */ {0, 0x0001, 0x8612}, /* White balance offset: Gr */ {0, 0x00FE, 0x8613}, /* White balance offset: B */ {0, 0x0000, 0x8614}, /* White balance offset: Gb */ {0, 0x0064, 0x8651}, /* R gain for white balance (L) */ {0, 0x0040, 0x8652}, /* Gr gain for white balance (L) */ {0, 0x0066, 0x8653}, /* B gain for white balance (L) */ {0, 0x0040, 0x8654}, /* Gb gain for white balance (L) */ {0, 0x0001, 0x863F}, /* Enable fixed gamma correction */ {0, 0x00A1, 0x8656}, /* Size - Window1: 256x256, Window2: 128x128 */ /* UV division: UV no change, Enable New edge enhancement */ {0, 0x0018, 0x8657}, /* Edge gain high threshold */ {0, 0x0020, 0x8658}, /* Edge gain low threshold */ {0, 0x000A, 0x8659}, /* Edge bandwidth high threshold */ {0, 0x0005, 0x865A}, /* Edge bandwidth low threshold */ {0, 0x0064, 0x8607}, /* UV filter enable */ {0, 0x0016, 0x8660}, {0, 0x0000, 0x86B0}, /* Bad pixels compensation address */ {0, 0x00DC, 0x86B1}, /* X coord for bad pixels compensation (L) */ {0, 0x0000, 0x86B2}, {0, 0x0009, 0x86B3}, /* Y coord for bad pixels compensation (L) */ {0, 0x0000, 0x86B4}, {0, 0x0001, 0x86B0}, {0, 0x00F5, 0x86B1}, {0, 0x0000, 0x86B2}, {0, 0x00C6, 0x86B3}, {0, 0x0000, 0x86B4}, {0, 0x0002, 0x86B0}, {0, 0x001C, 0x86B1}, {0, 0x0001, 0x86B2}, {0, 0x00D7, 0x86B3}, {0, 0x0000, 0x86B4}, {0, 0x0003, 0x86B0}, {0, 0x001C, 0x86B1}, {0, 0x0001, 0x86B2}, {0, 0x00D8, 0x86B3}, {0, 0x0000, 0x86B4}, {0, 0x0004, 0x86B0}, {0, 0x001D, 0x86B1}, {0, 0x0001, 0x86B2}, {0, 0x00D8, 0x86B3}, {0, 0x0000, 0x86B4}, {0, 0x001E, 0x8660}, /* READ { 0, 0x0000, 0x8608 } -> 0000: 13 */ /* READ { 0, 0x0000, 0x8609 } -> 0000: 28 */ /* READ { 0, 0x0000, 0x8610 } -> 0000: 05 */ /* READ { 0, 0x0000, 0x8611 } -> 0000: 25 */ /* READ { 0, 0x0000, 0x8612 } -> 0000: e1 */ /* READ { 0, 0x0000, 0x8613 } -> 0000: fa */ /* READ { 0, 0x0000, 0x8614 } -> 0000: f4 */ /* READ { 0, 0x0000, 0x8615 } -> 0000: e8 */ /* READ { 0, 0x0000, 0x8616 } -> 0000: 25 */ {0, 0, 0} }; static int config_spca508(struct usb_spca50x *spca50x) { struct usb_device *dev = spca50x->dev; int data1, data2; // Read frm global register the USB product and vendor IDs, just to // prove that we can communicate with the device. This works, which // confirms at we are communicating properly and that the device // is a 508. data1 = spca50x_reg_read(dev, 0, 0x8104, 1); if (data1 < 0) PDEBUG(1, "Error reading USB Vendor ID from Global register"); data2 = spca50x_reg_read(dev, 0, 0x8105, 1); if (data2 < 0) PDEBUG(1, "Error reading USB Vendor ID from Global register"); PDEBUG(1, "Read from GLOBAL: USB Vendor ID 0x%02x%02x", data2, data1); data1 = spca50x_reg_read(dev, 0, 0x8106, 1); if (data1 < 0) PDEBUG(1, "Error reading USB Product ID from Global register"); data2 = spca50x_reg_read(dev, 0, 0x8107, 1); if (data2 < 0) PDEBUG(1, "Error reading USB Product ID from Global register"); PDEBUG(1, "Read from GLOBAL: USB Product ID 0x%02x%02x", data2, data1); data1 = spca50x_reg_read(dev, 0, 0x8621, 1); if (data1 < 0) PDEBUG(1, "Error reading Window 1 Average Luminance from Global register"); PDEBUG(1, "Read from GLOBAL: Window 1 average luminance %3d", data1); switch (spca50x->desc) { case ViewQuestVQ110: { if (spca50x_write_vector(spca50x, spca508_init_data)) return -1; break; } case MicroInnovationIC200: case IntelEasyPCCamera: { if (spca50x_write_vector(spca50x, spca508cs110_init_data)) return -1; break; } case HamaUSBSightcam: { if (spca50x_write_vector(spca50x, spca508_sightcam_init_data)) return -1; break; } case HamaUSBSightcam2: { if (spca50x_write_vector(spca50x, spca508_sightcam2_init_data)) return -1; break; } case CreativeVista: { if (spca50x_write_vector(spca50x, spca508_vista_init_data)) return -1; break; } default: return -1; } return 0; // success } #endif /* SPCA508_INIT_H */ pwcbsd/spca5xx-20060402/drivers/usb/spca561.h000755 000423 000000 00000067101 10552536625 020707 0ustar00luigiwheel000000 000000 #ifndef SPCA561_INIT_H #define SPCA561_INIT_H /**************************************************************************** # Sunplus spca561 library # # Copyright (C) 2004 Michel Xhaard mxhaard@magic.fr # # # # This program is free software; you can redistribute it and/or modify # # it under the terms of the GNU General Public License as published by # # the Free Software Foundation; either version 2 of the License, or # # (at your option) any later version. # # # # This program is distributed in the hope that it will be useful, # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # # ****************************************************************************/ /* Initialization data I'm not very sure how to split initialization from open data chunks. For now, we'll consider everything as initialization */ /* Frame packet header offsets for the spca561 */ #define SPCA561_OFFSET_SNAP 1 #define SPCA561_OFFSET_TYPE 2 #define SPCA561_OFFSET_COMPRESS 3 #define SPCA561_OFFSET_FRAMSEQ 4 #define SPCA561_OFFSET_GPIO 5 #define SPCA561_OFFSET_USBBUFF 6 #define SPCA561_OFFSET_WIN2GRAVE 7 #define SPCA561_OFFSET_WIN2RAVE 8 #define SPCA561_OFFSET_WIN2BAVE 9 #define SPCA561_OFFSET_WIN2GBAVE 10 #define SPCA561_OFFSET_WIN1GRAVE 11 #define SPCA561_OFFSET_WIN1RAVE 12 #define SPCA561_OFFSET_WIN1BAVE 13 #define SPCA561_OFFSET_WIN1GBAVE 14 #define SPCA561_OFFSET_FREQ 15 #define SPCA561_OFFSET_VSYNC 16 #define SPCA561_OFFSET_DATA 1 #define SPCA561_INDEX_I2C_BASE 0x8800 #define SPCA561_SNAPBIT 0x20 #define SPCA561_SNAPCTRL 0x40 enum { Rev072A = 0, Rev012A, }; /******************* Camera Interface ***********************/ static int spca561_init(struct usb_spca50x *spca50x); static void spca561_start(struct usb_spca50x *spca50x); static void spca561_stop(struct usb_spca50x *spca50x); static __u16 spca561_setbrightness(struct usb_spca50x *spca50x); static __u16 spca561_getbrightness(struct usb_spca50x *spca50x); static __u16 spca561_setcontrast(struct usb_spca50x *spca50x); static __u16 spca561_getcontrast(struct usb_spca50x *spca50x); //static __u16 spca561_setcolors(struct usb_spca50x *spca50x); //static __u16 spca561_getcolors(struct usb_spca50x *spca50x); //static __u16 spca561_setexposure(struct usb_spca50x *spca50x); //static __u16 spca561_getexposure(struct usb_spca50x *spca50x); static void spca561_setAutobright(struct usb_spca50x *spca50x); static int spca561_config(struct usb_spca50x *spca50x); static void spca561_shutdown(struct usb_spca50x *spca50x); /******************************************************************/ static void spca561_InitI2c(struct usb_spca50x *spca50x, __u8 mode) { usb_wr_vend_dev(spca50x->dev, 0x00, 0x92, 0x8804, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0x00, mode, 0x8802, NULL, 0); } static void spca561_WriteI2c(struct usb_spca50x *spca50x, __u16 valeur, __u16 registre) { int retry = 60; __u8 DataLow = 0; __u8 DataHight = 0; __u8 Data = 0; DataLow = valeur & 0xFF; DataHight = (valeur >> 8) & 0xFF; usb_wr_vend_dev(spca50x->dev, 0x00, registre, 0x8801, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0x00, DataLow, 0x8805, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0x00, DataHight, 0x8800, NULL, 0); while (retry--) { usb_rd_vend_dev(spca50x->dev, 0x00, 0, 0x8803, &Data, 1); if (!Data) break; } } /****************** not in use **********************************/ static int spca561_ReadI2c(struct usb_spca50x *spca50x, __u16 registre, __u8 mode) { int retry = 60; unsigned char value = 0; unsigned char vallsb = 0; __u8 Data = 0; usb_wr_vend_dev(spca50x->dev, 0x00, 0x92, 0x8804, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0x00, registre, 0x8801, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0x00, (mode | 0x01), 0x8802, NULL, 0); while (retry--) { usb_rd_vend_dev(spca50x->dev, 0x00, 0, 0x8803, &Data, 1); if (!Data) break; } if (retry == 0) return -1; usb_rd_vend_dev(spca50x->dev, 0x00, 0, 0x8800, &value, 1); usb_rd_vend_dev(spca50x->dev, 0x00, 0, 0x8805, &vallsb, 1); return (int) value << 8 | vallsb; } static __u16 spca561_init_data[][3] = { {0, 0x0000, 0x8114}, // Software GPIO output data {0, 0x0001, 0x8114}, // Software GPIO output data {0, 0x0000, 0x8112}, // Some kind of reset {0, 0x0003, 0x8701}, // PCLK clock delay adjustment {0, 0x0001, 0x8703}, // HSYNC from cmos inverted {0, 0x0011, 0x8118}, // Enable and conf sensor {0, 0x0001, 0x8118}, // Conf sensor {0, 0x0092, 0x8804}, // I know nothing about these {0, 0x0010, 0x8802}, // 0x88xx registers, so I won't /*********************/ {0, 0x000d, 0x8805}, // sensor default setting {0, 0x0001, 0x8801}, // 1 <- 0x0d {0, 0x0000, 0x8800}, {0, 0x0018, 0x8805}, {0, 0x0002, 0x8801}, // 2 <- 0x18 {0, 0x0000, 0x8800}, {0, 0x0065, 0x8805}, {0, 0x0004, 0x8801}, // 4 <- 0x01 0x65 {0, 0x0001, 0x8800}, {0, 0x0021, 0x8805}, {0, 0x0005, 0x8801}, // 5 <- 0x21 {0, 0x0000, 0x8800}, {0, 0x00aa, 0x8805}, {0, 0x0007, 0x8801}, // 7 <- 0xaa {0, 0x0000, 0x8800}, {0, 0x0004, 0x8805}, {0, 0x0020, 0x8801}, // 0x20 <- 0x15 0x04 {0, 0x0015, 0x8800}, {0, 0x0002, 0x8805}, {0, 0x0039, 0x8801}, // 0x39 <- 0x02 {0, 0x0000, 0x8800}, {0, 0x0010, 0x8805}, {0, 0x0035, 0x8801}, // 0x35 <- 0x10 {0, 0x0000, 0x8800}, {0, 0x0049, 0x8805}, {0, 0x0009, 0x8801}, // 0x09 <- 0x10 0x49 {0, 0x0010, 0x8800}, {0, 0x000b, 0x8805}, {0, 0x0028, 0x8801}, // 0x28 <- 0x0b {0, 0x0000, 0x8800}, {0, 0x000f, 0x8805}, {0, 0x003b, 0x8801}, // 0x3b <- 0x0f {0, 0x0000, 0x8800}, {0, 0x0000, 0x8805}, {0, 0x003c, 0x8801}, // 0x3c <- 0x00 {0, 0x0000, 0x8800}, /**********************/ {0, 0x0018, 0x8601}, // Pixel/line selection for color separation {0, 0x0000, 0x8602}, // Optical black level for user setting {0, 0x0060, 0x8604}, // Optical black horizontal offset {0, 0x0002, 0x8605}, // Optical black vertical offset {0, 0x0000, 0x8603}, // Non-automatic optical black level {0, 0x0002, 0x865b}, // Horizontal offset for valid pixels {0, 0x0000, 0x865f}, // Vertical valid pixels window (x2) {0, 0x00b0, 0x865d}, // Horizontal valid pixels window (x2) {0, 0x0090, 0x865e}, // Vertical valid lines window (x2) {0, 0x00e0, 0x8406}, // Memory buffer threshold {0, 0x0000, 0x8660}, // Compensation memory stuff {0, 0x0002, 0x8201}, // Output address for r/w serial EEPROM {0, 0x0008, 0x8200}, // Clear valid bit for serial EEPROM {0, 0x0001, 0x8200}, // OprMode to be executed by hardware {0, 0x0007, 0x8201}, // Output address for r/w serial EEPROM {0, 0x0008, 0x8200}, // Clear valid bit for serial EEPROM {0, 0x0001, 0x8200}, // OprMode to be executed by hardware {0, 0x0010, 0x8660}, // Compensation memory stuff {0, 0x0018, 0x8660}, // Compensation memory stuff {0, 0x0004, 0x8611}, // R offset for white balance {0, 0x0004, 0x8612}, // Gr offset for white balance {0, 0x0007, 0x8613}, // B offset for white balance {0, 0x0000, 0x8614}, // Gb offset for white balance {0, 0x008c, 0x8651}, // R gain for white balance {0, 0x008c, 0x8652}, // Gr gain for white balance {0, 0x00b5, 0x8653}, // B gain for white balance {0, 0x008c, 0x8654}, // Gb gain for white balance {0, 0x0002, 0x8502}, // Maximum average bit rate stuff {0, 0x0011, 0x8802}, {0, 0x0087, 0x8700}, // Set master clock (96Mhz????) {0, 0x0081, 0x8702}, // Master clock output enable {0, 0x0000, 0x8500}, // Set image type (352x288 no compression) // Originally was 0x0010 (352x288 compression) {0, 0x0002, 0x865b}, // Horizontal offset for valid pixels {0, 0x0003, 0x865c}, // Vertical offset for valid lines /*************************/// sensor active {0, 0x0003, 0x8801}, // 0x03 <- 0x01 0x21 //289 {0, 0x0021, 0x8805}, {0, 0x0001, 0x8800}, {0, 0x0004, 0x8801}, // 0x04 <- 0x01 0x65 //357 {0, 0x0065, 0x8805}, {0, 0x0001, 0x8800}, {0, 0x0005, 0x8801}, // 0x05 <- 0x2f {0, 0x002f, 0x8805}, {0, 0x0000, 0x8800}, {0, 0x0006, 0x8801}, // 0x06 <- 0 {0, 0x0000, 0x8805}, {0, 0x0000, 0x8800}, {0, 0x000a, 0x8801}, // 0x0a <- 2 {0, 0x0002, 0x8805}, {0, 0x0000, 0x8800}, {0, 0x0009, 0x8801}, // 0x09 <- 0x1061 {0, 0x0061, 0x8805}, {0, 0x0010, 0x8800}, {0, 0x0035, 0x8801}, // 0x35 <-0x14 {0, 0x0014, 0x8805}, {0, 0x0000, 0x8800}, {0, 0x0030, 0x8112}, // ISO and drop packet enable {0, 0x0000, 0x8112}, // Some kind of reset ???? {0, 0x0009, 0x8118}, // Enable sensor and set standby {0, 0x0000, 0x8114}, // Software GPIO output data {0, 0x0000, 0x8114}, // Software GPIO output data {0, 0x0001, 0x8114}, // Software GPIO output data {0, 0x0000, 0x8112}, // Some kind of reset ??? {0, 0x0003, 0x8701}, {0, 0x0001, 0x8703}, {0, 0x0011, 0x8118}, {0, 0x0001, 0x8118}, /**************************/ {0, 0x0092, 0x8804}, {0, 0x0010, 0x8802}, {0, 0x000d, 0x8805}, {0, 0x0001, 0x8801}, {0, 0x0000, 0x8800}, {0, 0x0018, 0x8805}, {0, 0x0002, 0x8801}, {0, 0x0000, 0x8800}, {0, 0x0065, 0x8805}, {0, 0x0004, 0x8801}, {0, 0x0001, 0x8800}, {0, 0x0021, 0x8805}, {0, 0x0005, 0x8801}, {0, 0x0000, 0x8800}, {0, 0x00aa, 0x8805}, {0, 0x0007, 0x8801}, // mode 0xaa {0, 0x0000, 0x8800}, {0, 0x0004, 0x8805}, {0, 0x0020, 0x8801}, {0, 0x0015, 0x8800}, //mode 0x0415 {0, 0x0002, 0x8805}, {0, 0x0039, 0x8801}, {0, 0x0000, 0x8800}, {0, 0x0010, 0x8805}, {0, 0x0035, 0x8801}, {0, 0x0000, 0x8800}, {0, 0x0049, 0x8805}, {0, 0x0009, 0x8801}, {0, 0x0010, 0x8800}, {0, 0x000b, 0x8805}, {0, 0x0028, 0x8801}, {0, 0x0000, 0x8800}, {0, 0x000f, 0x8805}, {0, 0x003b, 0x8801}, {0, 0x0000, 0x8800}, {0, 0x0000, 0x8805}, {0, 0x003c, 0x8801}, {0, 0x0000, 0x8800}, {0, 0x0002, 0x8502}, {0, 0x0039, 0x8801}, {0, 0x0000, 0x8805}, {0, 0x0000, 0x8800}, {0, 0x0087, 0x8700}, //overwrite by start {0, 0x0081, 0x8702}, {0, 0x0000, 0x8500}, // { 0 , 0x0010 , 0x8500 }, -- Previous line was this {0, 0x0002, 0x865b}, {0, 0x0003, 0x865c}, /************************/ {0, 0x0003, 0x8801}, // 0x121-> 289 {0, 0x0021, 0x8805}, {0, 0x0001, 0x8800}, {0, 0x0004, 0x8801}, //0x165 -> 357 {0, 0x0065, 0x8805}, {0, 0x0001, 0x8800}, {0, 0x0005, 0x8801}, //0x2f //blanking control colonne {0, 0x002f, 0x8805}, {0, 0x0000, 0x8800}, {0, 0x0006, 0x8801}, //0x00 //blanking mode row {0, 0x0000, 0x8805}, {0, 0x0000, 0x8800}, {0, 0x000a, 0x8801}, //0x01 //0x02 {0, 0x0001, 0x8805}, {0, 0x0000, 0x8800}, {0, 0x0009, 0x8801}, // 0x1061 // setexposure times && pixel clock 0001 0 | 000 0110 0001 {0, 0x0061, 0x8805}, //61 31 {0, 0x0008, 0x8800}, // 08 {0, 0x0035, 0x8801}, // 0x14 // set gain general {0, 0x001F, 0x8805}, //0x14 {0, 0x0000, 0x8800}, {0, 0x0030, 0x8112}, {0, 0, 0} }; static void sensor_Reset(struct usb_spca50x *spca50x) { int err; err = spca50x_reg_write(spca50x->dev, 0, 0x8631, 0xC8); err = spca50x_reg_write(spca50x->dev, 0, 0x8634, 0xC8); err = spca50x_reg_write(spca50x->dev, 0, 0x8112, 0x00); err = spca50x_reg_write(spca50x->dev, 0, 0x8114, 0x00); err = spca50x_reg_write(spca50x->dev, 0, 0x8118, 0x21); spca561_InitI2c(spca50x, 0x14); spca561_WriteI2c(spca50x, 1, 0x0d); spca561_WriteI2c(spca50x, 0, 0x0d); } /************************* QC Express etch2 stuff ********************/ static __u16 Pb100_1map8300[][2] = { /* reg, value */ {0x8320, 0x3304}, {0x8303, 0x0125}, {0x8304, 0x0169}, {0x8328, 0x000b}, {0x833c, 0x0007}, {0x832f, 0x0f00}, //419 {0x8307, 0x00aa}, {0x8339, 0x0000}, {0x8335, 0x0018}, {0x8309, 0x2048}, {0x8301, 0x000d}, //3 {0x8302, 0x0018}, //e {0, 0} }; static __u16 Pb100_2map8300[][2] = { /* reg, value */ {0x8339, 0x0000}, {0x8307, 0x00aa}, {0, 0} }; static __u16 spca561_161rev12A_data1[][3] = { {0x00, 0x21, 0x8118}, //0x29 enable sensor {0x00, 0x01, 0x8114}, {0x00, 0x00, 0x8112}, {0x00, 0x92, 0x8804}, {0x00, 0x04, 0x8802}, }; static __u16 spca561_161rev12A_data2[][3] = { {0x00, 0x21, 0x8118}, //{ 0x00, 0x04, 0x8501 }, // {0x00, 0x00, 0x8114}, {0x00, 0x01, 0x8114}, // {0x00, 0x90, 0x8604}, {0x00, 0x00, 0x8605}, {0x00, 0xb0, 0x8603}, //b0 00 {0x00, 0x02, 0x8201}, {0x00, 0x08, 0x8200}, {0x00, 0x01, 0x8200}, {0x00, 0x07, 0x8201}, {0x00, 0x08, 0x8200}, {0x00, 0x01, 0x8200}, {0x00, 0x08, 0x8620}, {0x00, 0x0C, 0x8620}, {0x00, 0x00, 0x8610}, // *rouge {0x00, 0x00, 0x8611}, //3f *vert {0x00, 0x00, 0x8612}, // vert *bleu {0x00, 0x00, 0x8613}, //bleu *vert {0x00, 0x35, 0x8614}, // vert *rouge {0x00, 0x35, 0x8615}, //40 *vert {0x00, 0x35, 0x8616}, //7a *bleu {0x00, 0x35, 0x8617}, //40 *vert {0x00, 0xf0, 0x8505}, {0x00, 0x32, 0x850a}, {0x00, 0x10, 0x8500}, //11 {0x00, 0x07, 0x8601}, //7 18 {0x00, 0x07, 0x8602}, //7 00 {0x00, 0x0c, 0x8620}, //0c {0x00, 0x7a, 0x8616}, //7a no comments {0x00, 0x40, 0x8617}, //40 {0x00, 0xc8, 0x8631}, //c8 {0x00, 0xc8, 0x8634}, //c8 {0x00, 0x23, 0x8635}, //23 {0x00, 0x1f, 0x8636}, //1f {0x00, 0xdd, 0x8637}, //dd {0x00, 0xe1, 0x8638}, //e1 {0x00, 0x1d, 0x8639}, //1d {0x00, 0x21, 0x863a}, //21 {0x00, 0xe3, 0x863b}, //e3 {0x00, 0xdf, 0x863c}, //df {0, 0, 0} }; static void sensor_mapwrite(struct usb_spca50x *spca50x, __u16 sensormap[][2]) { int i = 0; __u8 usbval[] = { 0, 0 }; while (sensormap[i][0]) { usbval[0] = sensormap[i][1] & 0xff; usbval[1] = (sensormap[i][1] >> 8) & 0xff; usb_wr_vend_dev(spca50x->dev, 0x00, 0x00, sensormap[i][0], usbval, 2); i++; } } static int init_161rev12A(struct usb_spca50x *spca50x) { int err; __u8 Reg8391[] = { 0x23, 0x31, 0x10, 0x00, 0x3a, 0x00, 0x00, 0x00 }; //14 __u8 Reg8307[] = { 0xaa, 0x00 }; err = spca50x_reg_write(spca50x->dev, 0, 0x8620, 0x00); // sensor_Reset(spca50x); spca50x_write_vector(spca50x, spca561_161rev12A_data1); sensor_mapwrite(spca50x, Pb100_1map8300); spca50x_write_vector(spca50x, spca561_161rev12A_data2); sensor_mapwrite(spca50x, Pb100_2map8300); err = spca50x_reg_write(spca50x->dev, 0, 0x8700, 0x85); // 0x27 clock usb_wr_vend_dev(spca50x->dev, 0, 0, 0x8391, Reg8391, 8); usb_wr_vend_dev(spca50x->dev, 0, 0, 0x8390, Reg8391, 8); err = spca50x_reg_write(spca50x->dev, 0, 0x8112, 0x10 | 0x20); err = spca50x_reg_write(spca50x->dev, 0, 0x850b, 0x03); err = spca50x_reg_write(spca50x->dev, 0, 0x8112, 0x00); //set_alternate setting 0 err = spca50x_reg_write(spca50x->dev, 0, 0x8118, 0x29); err = spca50x_reg_write(spca50x->dev, 0, 0x8114, 0x00); //set_alternate setting 7 spca50x_write_vector(spca50x, spca561_161rev12A_data2); usb_wr_vend_dev(spca50x->dev, 0, 0, 0x8307, Reg8307, 2); err = spca50x_reg_write(spca50x->dev, 0, 0x8700, 0x85); // 0x27 clock usb_wr_vend_dev(spca50x->dev, 0, 0, 0x8391, Reg8391, 8); usb_wr_vend_dev(spca50x->dev, 0, 0, 0x8390, Reg8391, 8); err = spca50x_reg_write(spca50x->dev, 0, 0x8112, 0x10 | 0x20); err = spca50x_reg_write(spca50x->dev, 0, 0x850b, 0x03); err = spca50x_reg_write(spca50x->dev, 0, 0x8112, 0x20); // return 0; } /************************* End spca561rev12A stuff **********************/ /************************* Core spca561 stuff ************************/ static int spca561_init(struct usb_spca50x *spca50x) { int err; switch (spca50x->chip_revision) { case Rev072A: PDEBUG(0, "Find spca561 USB Product ID %x", spca50x->customid); spca50x_write_vector(spca50x, spca561_init_data); break; case Rev012A: PDEBUG(0, "Find spca561 USB Product ID %x", spca50x->customid); err = init_161rev12A(spca50x); break; default: PDEBUG(0, "Error reading USB Product ID from Global register"); break; } return 0; } #if 0 static void spca561_dumpSensor(struct usb_spca50x *spca50x) { int i; __u8 RegSens[] = { 0, 0 }; switch (spca50x->chip_revision) { case Rev072A: /*dump sensor registers */ for (i = 0; i < 0x36; i++) { /* mode 0x10 561, 0x14 mapped */ err = spca561_ReadI2c(spca50x, i, 0x10); PDEBUG(0, "reading Sensor i2c register 0x%02X -> 0x%04X", i, err); } break; case Rev012A: /* Sensor mapped registers */ for (i = 0; i < 0x36; i++) { usb_rd_vend_dev(spca50x->dev, 0, 0, 0x8300 + i, RegSens, 2); PDEBUG(0, "reading Sensor map0x8300 register 0x%02X -> 0x%04X", i, RegSens[1] << 8 | RegSens[0]); } break; } } #endif static void spca561_start(struct usb_spca50x *spca50x) { int err; int Clck = 0; __u8 Reg8307[] = { 0xaa, 0x00 }; __u8 Reg8391[] = { 0x90, 0x31, 0x0b, 0x00, 0x25, 0x00, 0x00, 0x00 }; //90 31 0c switch (spca50x->chip_revision) { case Rev072A: switch (spca50x->mode) { case 0: case 1: Clck = 0x25; break; case 2: Clck = 0x22; break; case 3: Clck = 0x21; break; default: Clck = 0x25; break; } err = spca50x_reg_write(spca50x->dev, 0, 0x8500, spca50x->mode); // mode err = spca50x_reg_write(spca50x->dev, 0, 0x8700, Clck); // 0x27 clock err = spca50x_reg_write(spca50x->dev, 0, 0x8112, 0x10 | 0x20); break; case Rev012A: switch (spca50x->mode) { case 0: //Clck =(spca50x->customid == 0x403b) ? 0x8a : 0x8f; Clck = 0x8a; break; case 1: Clck = 0x8a; break; case 2: Clck = 0x85; Reg8391[1] = 0x22; // increase pixel clock increase time exposure break; case 3: Clck = 0x83; Reg8391[1] = 0x22; break; default: Clck = 0x25; break; } if (compress && spca50x->mode <= 1) { // this is correct for 320x240; it also works at 352x288 // hell, I don't even know what this value means :) Clck = 0x83; err = spca50x_reg_write(spca50x->dev, 0, 0x8500, 0x10 + spca50x->mode); } else { // I couldn't get the compression to work below 320x240 // Fortunately at these resolutions the bandwidth is sufficient // to push raw frames at ~20fps err = spca50x_reg_write(spca50x->dev, 0, 0x8500, spca50x->mode); } // -- qq@kuku.eu.org usb_wr_vend_dev(spca50x->dev, 0, 0, 0x8307, Reg8307, 2); err = spca50x_reg_write(spca50x->dev, 0, 0x8700, Clck); // 0x8f 0x85 0x27 clock usb_wr_vend_dev(spca50x->dev, 0, 0, 0x8391, Reg8391, 8); usb_wr_vend_dev(spca50x->dev, 0, 0, 0x8390, Reg8391, 8); spca50x->exposure = ((Reg8391[1]) << 8) | Reg8391[0]; //set exposure with clock err = spca50x_reg_write(spca50x->dev, 0, 0x8112, 0x10 | 0x20); err = spca50x_reg_write(spca50x->dev, 0, 0x850b, 0x03); err = spca561_setcontrast(spca50x); break; default: PDEBUG(0, "Error reading USB Product ID from Global register"); break; } } static void spca561_stop(struct usb_spca50x *spca50x) { int err; err = spca50x_reg_write(spca50x->dev, 0, 0x8112, 0x20); // } static __u16 spca561_setbrightness(struct usb_spca50x *spca50x) { __u8 value = 0; value = spca50x->brightness >> 9; switch (spca50x->chip_revision) { case Rev072A: usb_wr_vend_dev(spca50x->dev, 0, value, 0x8611, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0, value, 0x8612, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0, value, 0x8613, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0, value, 0x8614, NULL, 0); break; case Rev012A: usb_wr_vend_dev(spca50x->dev, 0, value, 0x8615, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0, value, 0x8614, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0, value, 0x8616, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0, value, 0x8617, NULL, 0); break; } return 0; } static __u16 spca561_getbrightness(struct usb_spca50x *spca50x) { __u8 value = 0; __u16 tot = 0; switch (spca50x->chip_revision) { case Rev072A: usb_rd_vend_dev(spca50x->dev, 0, 0, 0x8611, &value, 1); tot += value; usb_rd_vend_dev(spca50x->dev, 0, 0, 0x8612, &value, 1); tot += value; usb_rd_vend_dev(spca50x->dev, 0, 0, 0x8613, &value, 1); tot += value; usb_rd_vend_dev(spca50x->dev, 0, 0, 0x8614, &value, 1); tot += value; spca50x->brightness = tot << 7; break; case Rev012A: usb_rd_vend_dev(spca50x->dev, 0, 0, 0x8615, &value, 1); spca50x->brightness = value << 9; break; } return spca50x->brightness; } static __u16 spca561_setcontrast(struct usb_spca50x *spca50x) { __u8 lowb = 0; int expotimes = 0; int pixelclk = 0; __u8 Reg8391[] = { 0x90, 0x31, 0x0b, 0x00, 0x25, 0x00, 0x00, 0x00 }; switch (spca50x->chip_revision) { case Rev072A: lowb = (spca50x->contrast >> 8) & 0xFF; usb_wr_vend_dev(spca50x->dev, 0, lowb, 0x8651, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0, lowb, 0x8652, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0, lowb, 0x8653, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0, lowb, 0x8654, NULL, 0); break; case Rev012A: lowb = (spca50x->contrast >> 10) & 0x7F; if (lowb < 4) lowb = 3; pixelclk = spca50x->exposure & 0xf800; spca50x->exposure = ((spca50x->contrast >> 5) & 0x07ff) | pixelclk; expotimes = spca50x->exposure & 0x07ff; Reg8391[0] = expotimes & 0xff; Reg8391[1] = ((pixelclk >> 8) & 0xf8) | ((expotimes >> 8) & 0x07); Reg8391[2] = lowb; PDEBUG(4, "Set Exposure 0x%02x 0x%02x gain 0x%02x", Reg8391[0], Reg8391[1], Reg8391[2]); usb_wr_vend_dev(spca50x->dev, 0, 0, 0x8390, Reg8391, 8); break; } return 0; } static __u16 spca561_getcontrast(struct usb_spca50x *spca50x) { __u8 value = 0; __u16 tot = 0; __u8 contrast = 0x0b; __u8 RegSens[] = { 0, 0 }; switch (spca50x->chip_revision) { case Rev072A: value = 0; usb_rd_vend_dev(spca50x->dev, 0, 0, 0x8651, &value, 1); tot += value; usb_rd_vend_dev(spca50x->dev, 0, 0, 0x8652, &value, 1); tot += value; usb_rd_vend_dev(spca50x->dev, 0, 0, 0x8653, &value, 1); tot += value; usb_rd_vend_dev(spca50x->dev, 0, 0, 0x8654, &value, 1); tot += value; spca50x->contrast = tot << 6; break; case Rev012A: usb_wr_vend_dev(spca50x->dev, 0, 0, 0x8335, &contrast, 1); /* always 0x8335 return 0 */ usb_rd_vend_dev(spca50x->dev, 0, 0, 0x8335, RegSens, 2); spca50x->contrast = (contrast & 0x7f) << 10; PDEBUG(2, "Get constrast 0x8335 0x%04x", RegSens[1] << 8 | RegSens[0]); break; } PDEBUG(4,"get contrast %d\n",spca50x->contrast); return spca50x->contrast; } static int spca561_config(struct usb_spca50x *spca50x) { __u8 data1, data2; // Read frm global register the USB product and vendor IDs, just to // prove that we can communicate with the device. This works, which // confirms at we are communicating properly and that the device // is a 561. usb_rd_vend_dev(spca50x->dev, 0, 0, 0x8104, &data1, 1); usb_rd_vend_dev(spca50x->dev, 0, 0, 0x8105, &data2, 1); PDEBUG(1, "Read from GLOBAL: USB Vendor ID 0x%02x%02x", data2, data1); usb_rd_vend_dev(spca50x->dev, 0, 0, 0x8106, &data1, 1); usb_rd_vend_dev(spca50x->dev, 0, 0, 0x8107, &data2, 1); PDEBUG(1, "Read from GLOBAL: USB Product ID 0x%02x%02x", data2, data1); spca50x->customid = ((data2 << 8) | data1) & 0xffff; switch (spca50x->customid) { case 0x7004: case 0xa001: case 0x0815: case 0x0561: case 0xcdee: case 0x7e50: spca50x->chip_revision = Rev072A; break; case 0x0928: case 0x0929: case 0x092a: case 0x403b: case 0x092b: case 0x092c: spca50x->chip_revision = Rev012A; break; default: PDEBUG(0, "Spca561 chip Unknow Contact the Author"); return -EINVAL; break; } memset(spca50x->mode_cam, 0x00, TOTMODE * sizeof(struct mwebcam)); /* XXX error, SIF and CIF swapped */ spca50x->mode_cam[SIF].width = 352; spca50x->mode_cam[SIF].height = 288; spca50x->mode_cam[SIF].t_palette = P_RAW | P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[SIF].pipe = 1023; spca50x->mode_cam[SIF].method = 0; spca50x->mode_cam[SIF].mode = 0; spca50x->mode_cam[CIF].width = 320; spca50x->mode_cam[CIF].height = 240; spca50x->mode_cam[CIF].t_palette = P_RAW | P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[CIF].pipe = 1023; spca50x->mode_cam[CIF].method = 0; spca50x->mode_cam[CIF].mode = 1; spca50x->mode_cam[QPAL].width = 192; spca50x->mode_cam[QPAL].height = 144; spca50x->mode_cam[QPAL].t_palette = P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[QPAL].pipe = 1023; spca50x->mode_cam[QPAL].method = 1; spca50x->mode_cam[QPAL].mode = 1; spca50x->mode_cam[QSIF].width = 176; spca50x->mode_cam[QSIF].height = 144; spca50x->mode_cam[QSIF].t_palette = P_RAW | P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[QSIF].pipe = 1023; spca50x->mode_cam[QSIF].method = 0; spca50x->mode_cam[QSIF].mode = 2; spca50x->mode_cam[QCIF].width = 160; spca50x->mode_cam[QCIF].height = 120; spca50x->mode_cam[QCIF].t_palette = P_RAW | P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[QCIF].pipe = 1023; spca50x->mode_cam[QCIF].method = 0; spca50x->mode_cam[QCIF].mode = 3; return 0; // success } static void spca561_shutdown(struct usb_spca50x *spca50x) { usb_wr_vend_dev(spca50x->dev, 0, 0, 0x8114, NULL, 0); } static void spca561_setAutobright(struct usb_spca50x *spca50x) { int expotimes = 0; int pixelclk = 0; int gainG = 0; __u8 R, Gr, Gb, B; int y; __u8 luma_mean = 110; __u8 luma_delta = 20; __u8 spring = 4; switch (spca50x->chip_revision) { case Rev072A: usb_rd_vend_dev(spca50x->dev, 0, 0, 0x8621, &Gr, 1); usb_rd_vend_dev(spca50x->dev, 0, 0, 0x8622, &R, 1); usb_rd_vend_dev(spca50x->dev, 0, 0, 0x8623, &B, 1); usb_rd_vend_dev(spca50x->dev, 0, 0, 0x8624, &Gb, 1); y = (77 * R + 75 * (Gr + Gb) + 29 * B) >> 8; //u= (128*B-(43*(Gr+Gb+R))) >> 8; //v= (128*R-(53*(Gr+Gb))-21*B) >> 8; //PDEBUG(0,"reading Y %d U %d V %d ",y,u,v); if ((y < (luma_mean - luma_delta)) || (y > (luma_mean + luma_delta))) { expotimes = spca561_ReadI2c(spca50x, 0x09, 0x10); pixelclk = 0x0800; expotimes = expotimes & 0x07ff; //PDEBUG(0,"Exposition Times 0x%03X Clock 0x%04X ",expotimes,pixelclk); gainG = spca561_ReadI2c(spca50x, 0x35, 0x10); //PDEBUG(0,"reading Gain register %d",gainG); expotimes += ((luma_mean - y) >> spring); gainG += ((luma_mean - y) / 50); // PDEBUG(0 , "compute expotimes %d gain %d",expotimes,gainG); if (gainG > 0x3F) gainG = 0x3f; else if (gainG < 4) gainG = 3; spca561_WriteI2c(spca50x, (__u16) gainG, 0x35); if (expotimes >= 0x0256) expotimes = 0x0256; else if (expotimes < 4) { expotimes = 3; } spca561_WriteI2c(spca50x, (__u16) (expotimes | pixelclk), 0x09); } break; case Rev012A: /* sensor registers is access and memory mapped to 0x8300 */ /* readind all 0x83xx block the sensor */ /* The data from the header seem wrong where is the luma and chroma mean value at the moment set exposure in contrast set */ ; break; default: break; } } #endif pwcbsd/spca5xx-20060402/drivers/usb/spca5xx.c000755 000423 000000 00000546660 10553461761 021126 0ustar00luigiwheel000000 000000 /* * SPCA5xx based usb camera driver (currently supports * yuv native stream spca501a, spca501c, spca505, spca508, spca506 * jpeg native stream spca500, spca551, spca504a, spca504b, spca533a, spca536a, zc0301, zc0302, cx11646, sn9c102p * bayer native stream spca561a, sn9c101, sn9c102, tv8532 ). * Z-star Vimicro chips zc0301 zc0301P zc0302 * Sunplus spca501a, spca501c, spca505, spca508, spca506, spca500, spca551, spca504a, spca504b, spca533a, spca536a * Sonix sn9c101, sn9c102, sn9c102p sn9c105 sn9c120 * Conexant cx11646 * Transvision tv_8532 * Etoms Et61x151 Et61x251 * Pixat Pac207-BCA-32 * SPCA5xx version by Michel Xhaard * Based on : * SPCA50x version by Joel Crisp * OmniVision OV511 Camera-to-USB Bridge Driver * Copyright (c) 1999-2000 Mark W. McClelland * Kernel 2.6.x port Michel Xhaard && Reza Jelveh (feb 2004) * Based on the Linux CPiA driver written by Peter Pregler, * Scott J. Bertin and Johannes Erdfelt. * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ #if defined(__FreeBSD__) /* * includes for FreeBSD kernel */ #define wait_event_interruptible(wq, condition) 1 /* always fail ... ok? */ #include "pwc.h" #endif /* !__FreeBSD */ #include "spca5xx.h" #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) # undef CONFIG_VIDEO_PROC_FS # undef CONFIG_PROC_FS #endif //#define RH9_REMAP 1 #include "spcadecoder.h" #include "jpeg_qtables.h" /* Video Size 640 x 480 x 4 bytes for RGB */ #define MAX_FRAME_SIZE (640 * 480 * 4) #define MAX_DATA_SIZE (MAX_FRAME_SIZE + sizeof(struct timeval)) #if defined(__FreeBSD__) static int compress = 0; #else /* !__FreeBSD__ */ /* Hardware auto exposure / whiteness (PC-CAM 600) */ static int autoexpo = 1; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,5) /* Video device number (-1 is first available) */ static int video_nr = -1; #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,5) */ #endif /* !__FreeBSD__ */ /* 0=no debug messages * 1=init/detection/unload and other significant messages, * 2=some warning messages * 3=config/control function calls * 4=most function calls and data parsing messages * 5=highly repetitive mesgs * NOTE: This should be changed to 0, 1, or 2 for production kernels */ static int debug = 0; /* XXX on freebsd, this is overridden by pwcdebug */ /* Force image to be read in RGB instead of BGR. This option allow * programs that expect RGB data (e.g. gqcam) to work with this driver. */ static int force_rgb = 0; static int gamma = 3; static int OffRed = 0; static int OffBlue = 0; static int OffGreen = 0; static int GRed = 256; static int GBlue = 256; static int GGreen = 256; static int usbgrabber = 0; #ifdef SPCA50X_ENABLE_COMPRESSION /* Enable compression. This is for experimentation only; compressed images * still cannot be decoded yet. */ static int compress = 0; #endif /* SPCA50X_ENABLE_COMPRESSION */ #ifdef SPCA5XX_ENABLE_REGISTERPLAY static int RegAddress = 0; static int RegValue = 0; static int RegStrobe = 0; #endif /* SPCA5XX_ENABLE_REGISTERPLAY */ #if !defined(__FreeBSD__) /* Initial brightness & contrast (for debug purposes) */ static int bright = 0x80; static int contrast = 0x60; #endif /* !__FreeBSD__ */ /* Parameter that enables you to set the minimal suitable bpp */ static int min_bpp = 0; /* Parameter defines the average luminance that should be kept */ static int lum_level = 0x2d; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 9) module_param(autoexpo, int, 0644); module_param(debug, int, 0644); module_param(force_rgb, int, 0644); module_param(gamma, int, 0644); module_param(OffRed, int, 0644); module_param(OffBlue, int, 0644); module_param(OffGreen, int, 0644); module_param(GRed, int, 0644); module_param(GBlue, int, 0644); module_param(GGreen, int, 0644); #ifdef SPCA50X_ENABLE_COMPRESSION module_param(compress, int, 0644); #endif /* SPCA50X_ENABLE_COMPRESSION */ module_param(bright, int, 0444); module_param(contrast, int, 0444); module_param(min_bpp, int, 0444); module_param(lum_level, int, 0444); module_param(usbgrabber, int, 0444); #ifdef SPCA5XX_ENABLE_REGISTERPLAY module_param(RegAddress, int, 0644); module_param(RegValue, int, 0644); module_param(RegStrobe, int, 0644); #endif /* SPCA5XX_ENABLE_REGISTERPLAY */ #else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 9) */ MODULE_PARM(autoexpo, "i"); MODULE_PARM(debug, "i"); MODULE_PARM(force_rgb, "i"); MODULE_PARM(gamma, "i"); MODULE_PARM(OffRed, "i"); MODULE_PARM(OffBlue, "i"); MODULE_PARM(OffGreen, "i"); MODULE_PARM(GRed, "i"); MODULE_PARM(GBlue, "i"); MODULE_PARM(GGreen, "i"); #ifdef SPCA50X_ENABLE_COMPRESSION MODULE_PARM(compress, "i"); #endif /* SPCA50X_ENABLE_COMPRESSION */ MODULE_PARM(bright, "i"); MODULE_PARM(contrast, "i"); MODULE_PARM(min_bpp, "i"); MODULE_PARM(lum_level, "0-255i"); MODULE_PARM(usbgrabber, "i"); #endif /***************/ MODULE_PARM_DESC(autoexpo, "Enable/Disable auto exposure (default=1: enabled) (PC-CAM 600/Zc03xx/spca561a/Etoms Only !!!)"); MODULE_PARM_DESC(debug, "Debug level: 0=none, 1=init/detection, 2=warning, 3=config/control, 4=function call, 5=max"); MODULE_PARM_DESC(force_rgb, "Read RGB instead of BGR"); MODULE_PARM_DESC(gamma, "gamma setting range 0 to 7 3-> gamma=1"); MODULE_PARM_DESC(OffRed, "OffRed setting range -128 to 128"); MODULE_PARM_DESC(OffBlue, "OffBlue setting range -128 to 128"); MODULE_PARM_DESC(OffGreen, "OffGreen setting range -128 to 128"); MODULE_PARM_DESC(GRed, "Gain Red setting range 0 to 512 /256 "); MODULE_PARM_DESC(GBlue, "Gain Blue setting range 0 to 512 /256 "); MODULE_PARM_DESC(GGreen, "Gain Green setting range 0 to 512 /256 "); #ifdef SPCA50X_ENABLE_COMPRESSION MODULE_PARM_DESC(compress, "Turn on/off compression (not functional yet)"); #endif /* SPCA50X_ENABLE_COMPRESSION */ MODULE_PARM_DESC(bright, "Initial brightness factor (0-255) not know by all webcams !!"); MODULE_PARM_DESC(contrast, "Initial contrast factor (0-255) not know by all webcams !!"); MODULE_PARM_DESC(min_bpp, "The minimal color depth that may be set (default 0)"); MODULE_PARM_DESC(lum_level, "Luminance level for brightness autoadjustment (default 32)"); MODULE_PARM_DESC(usbgrabber, "Is a usb grabber 0x0733:0x0430 ? (default 1) "); #ifdef SPCA5XX_ENABLE_REGISTERPLAY MODULE_PARM_DESC(RegAddress, "Register Address of PAC207"); MODULE_PARM_DESC(RegValue, "Register Value for PAC207"); MODULE_PARM_DESC(RegStrobe, "Strobe to read or write a register 1=write, 2=read"); #endif /* SPCA5XX_ENABLE_REGISTERPLAY */ /****************/ MODULE_AUTHOR ("Michel Xhaard based on spca50x driver by Joel Crisp ,ov511 driver by Mark McClelland "); MODULE_DESCRIPTION("SPCA5XX USB Camera Driver"); MODULE_LICENSE("GPL"); /********************************************************************** * List of known SPCA50X-based cameras **********************************************************************/ /* Camera type jpeg yuvy yyuv yuyv grey gbrg*/ struct palette_list Plist[] = { {JPEG, "JPEG"}, {JPGH, "JPEG"}, {JPGC, "JPEG"}, {JPGS, "JPEG"}, {JPGM, "JPEG"}, {YUVY, "YUVY"}, {YYUV, "YYUV"}, {YUYV, "YUYV"}, {GREY, "GREY"}, {GBRG, "GBRG"}, {SN9C, "SN9C"}, {GBGR, "GBGR"}, {S561, "S561"}, {PGBRG, "GBRG"}, /* XXX this is actually bayer offset by 1 */ {-1, NULL} }; /* * Let's include the initialization data for each camera type */ #include "spcausb.h" #include "spca500_init.h" #include "spca501_init.h" #include "spca505_init.h" #include "spca506.h" #include "spca508_init.h" #include "spca561.h" #include "sp5xxfw2.h" #include "sonix.h" #include "zc3xx.h" #include "cx11646.h" #include "tv8532.h" #include "et61xx51.h" #include "mr97311.h" #include "pac207.h" /* Mode list for spca50x on external input */ /* Must be in descending order as the closest match which is equal or smaller than * the requested size is returned */ #if 0 /* Trashcan before goes out */ (bridge) == BRIDGE_SONIX ? sonix_ext_modes : (bridge) == BRIDGE_ZC3XX ? zc3xx_ext_modes : (bridge) == BRIDGE_ETOMS ? etoms_ext_modes : (bridge) == BRIDGE_SPCA561 ? spca561_ext_modes : (bridge) == BRIDGE_SN9CXXX ? sn9c102p_ext_modes : (bridge) == BRIDGE_SPCA504 ? spca504_ext_modes : (bridge) == BRIDGE_SPCA504B ? spca504_ext_modes : (bridge) == BRIDGE_SPCA504C ? spca504_pccam600_ext_modes : (bridge) == BRIDGE_SPCA533 ? spca533_ext_modes : (bridge) == BRIDGE_SPCA536 ? spca536_ext_modes : static __u16 pcam_ext_modes[][6] = { /* x , y , Code, Value (6), Value (7), pipe */ {1280, 1024, 0x00, 0, 0, 1023}, {640, 480, 0x01, 0, 0, 1023}, {384, 288, 0x11, 16, 12, 1023}, {352, 288, 0x11, 18, 12, 1023}, {320, 240, 0x02, 0, 0, 896}, {192, 144, 0x12, 8, 6, 896}, {176, 144, 0x12, 9, 6, 896}, {0, 0, 0, 0, 0} }; static __u16 zc3xx_ext_modes[][6] = { /* x , y , Code, x multiplier, y multiplier, pipe */ {640, 480, 0x00, 0, 0, 1023}, // altersetting 7 endpoint 0x01 {352, 288, 0x10, 0, 0, 1023}, //0x00 SIF sensor {320, 240, 0x01, 0, 0, 1023}, {176, 144, 0x11, 0, 0, 1023}, //0x01 {0, 0, 0, 0, 0, 0} }; static __u16 sonix_ext_modes[][6] = { /* x , y , Code, x multiplier, y multiplier, pipe */ {640, 480, 0x00, 0, 0, 1023}, {352, 288, 0x00, 0, 0, 1023}, {320, 240, 0x01, 0, 0, 1023}, {176, 144, 0x01, 0, 0, 1023}, {160, 120, 0x02, 0, 0, 1023}, {0, 0, 0, 0, 0, 0} }; static __u16 etoms_ext_modes[][6] = { /* x , y , Code, x, y, pipe */ {352, 288, 0x00, 0, 0, 1000}, {320, 240, 0x01, 0, 0, 1000}, {176, 144, 0x01, 0, 0, 1000}, {0, 0, 0, 0, 0, 0} }; static __u16 spca561_ext_modes[][6] = { {352, 288, 0x00, 0x27, 0x00, 1023}, {320, 240, 0x01, 0x27, 0x00, 1023}, {176, 144, 0x02, 0x23, 0x00, 1023}, // software mode hardware seem buggy slow shift in video {160, 120, 0x03, 0x23, 0x00, 1023}, {0, 0, 0, 0, 0} }; static __u16 sn9c102p_ext_modes[][6] = { /* x , y , Code, x, y, pipe */ {640, 480, 0x00, 0, 0, 1023}, {352, 288, 0x10, 0, 0, 1023}, {320, 240, 0x01, 0, 0, 1023}, {176, 144, 0x11, 0, 0, 1023}, {0, 0, 0, 0, 0, 0} static __u16 spca533_ext_modes[][6] = { /* x , y , Code, Value (6), Value (7), pipe */ //{ 640, 480, 0x41, 1, 0, 1023 }, {464, 480, 0x01, 0, 0, 1023}, //PocketDVII unscaled resolution aspect ratio need to expand x axis {464, 352, 0x01, 0, 0, 1023}, //Gsmart LCD3 feature good aspect ratio {384, 288, 0x11, 5, 4, 1023}, {352, 288, 0x11, 7, 4, 1023}, {320, 240, 0x02, 0, 0, 1023}, {192, 144, 0x12, 8, 6, 1023}, {176, 144, 0x12, 9, 6, 1023}, //{ 160, 120, 0x22, 1, 1, 1023 }, //{ 320, 128, 0x03, 0, 0, 1023 }, /* don't work with megapix V4*/ {0, 0, 0, 0, 0} }; static __u16 spca536_ext_modes[][6] = { /* x , y , Code, Value (6), Value (7), pipe */ {640, 480, 0x01, 1, 0, 1023}, //{ 464, 480, 0x01, 0, 0, 1023 }, //{ 464, 352, 0x01, 0, 0, 1023 }, {384, 288, 0x11, 5, 4, 1023}, {352, 288, 0x11, 7, 4, 1023}, {320, 240, 0x02, 0, 0, 896}, {192, 144, 0x12, 8, 6, 896}, {176, 144, 0x12, 9, 6, 896}, {0, 0, 0, 0, 0} }; static __u16 spca504_ext_modes[][6] = { /* x , y , Code, Value (6), Value (7), pipe */ {640, 480, 0x01, 0, 0, 1023}, {384, 288, 0x11, 16, 12, 1023}, {352, 288, 0x11, 18, 12, 1023}, {320, 240, 0x02, 0, 0, 896}, {192, 144, 0x12, 8, 6, 896}, {176, 144, 0x12, 9, 6, 896}, //{ 160, 120, 0x22, 1, 1, 896 }, {0, 0, 0, 0, 0} }; static __u16 spca504_pccam600_ext_modes[][6] = { /* x , y , Code, Value (6), Value (7), pipe */ {1024, 768, 0x00, 0, 0, 1023}, {640, 480, 0x01, 1, 0, 1023}, {352, 288, 0x02, 2, 0, 896}, {320, 240, 0x03, 3, 0, 896}, {176, 144, 0x04, 4, 0, 768}, /* { 160, 120, 0x05, 5, 0, 768 }, */ {0, 0, 0, 0, 0} }; }; #endif #define GET_EXT_MODES(bridge) (\ (bridge) == BRIDGE_SPCA500 ? spca500_ext_modes : \ (bridge) == BRIDGE_SPCA501 ? spca501_ext_modes : \ (bridge) == BRIDGE_SPCA506 ? spca506_ext_modes : \ (bridge) == BRIDGE_SPCA508 ? spca508_ext_modes : \ (bridge) == BRIDGE_CX11646 ? cx11646_ext_modes : \ (bridge) == BRIDGE_TV8532 ? tv8532_ext_modes : \ spca50x_ext_modes) /* code is used for harware low nibble and software hight nibble */ static __u16 cx11646_ext_modes[][6] = { /* x , y , Code, x multiplier, y multiplier, pipe */ {640, 480, 0x00, 0, 0, 1023}, // altersetting 7 endpoint 0x01 {352, 288, 0x01, 0, 0, 1023}, {320, 240, 0x02, 0, 0, 1023}, {176, 144, 0x03, 0, 0, 640}, // alt 4 // {160, 120, 0x04, 0, 0, 512}, // alt 3 only works with external decoding {0, 0, 0, 0, 0, 0} }; static __u16 spca500_ext_modes[][6] = { /* x , y , Code, x multiplier, y multiplier, pipe */ {640, 480, 0x00, 40, 30, 1023}, {352, 288, 0x00, 22, 18, 1023}, {320, 240, 0x01, 40, 30, 1023}, {176, 144, 0x01, 22, 18, 1023}, /* 160x120 disable jpeg %16 */ {0, 0, 0, 0, 0, 0} }; static __u16 spca501_ext_modes[][6] = { /* x , y , Code, Value (6), Value (7), pipe */ {640, 480, 0, 0x94, 0x004A, 1023}, {352, 288, 0x10, 0x94, 0x004A, 1023}, {320, 240, 0, 0x94, 0x104A, 896}, {176, 144, 0x10, 0x94, 0x104A, 896}, {160, 120, 0, 0x94, 0x204A, 384}, {0, 0, 0, 0, 0} }; /* default value used for spca505 and spca505b */ static __u16 spca50x_ext_modes[][6] = { /* x , y , Code, Value (6), Value (7), pipe */ {640, 480, 0, 0x10, 0x10, 1023}, /* Tested spca505b+VGA sensor */ {352, 288, 1, 0x1a, 0x1a, 1023}, /* Tested */ {320, 240, 2, 0x1c, 0x1d, 896}, /* Tested 20:01:2004 */ {176, 144, 4, 0x34, 0x34, 512}, /* Tested */ {160, 120, 5, 0x40, 0x40, 384}, /* Tested */ {0, 0, 0, 0, 0} }; static __u16 spca506_ext_modes[][6] = { /* In this table, element 3 (clk) controls the * clock, and gets written to 0x8700. */ /* x , y , Code, clk, n/a, pipe */ {640, 480, 0x00, 0x10, 0x10, 1023}, {352, 288, 0x01, 0x1a, 0x1a, 1023}, {320, 240, 0x02, 0x1c, 0x1c, 1023}, //896 {176, 144, 0x04, 0x34, 0x34, 1023}, {160, 120, 0x05, 0x40, 0x40, 1023}, {0, 0, 0, 0, 0} }; // 1a 1b 20 static __u16 spca508_ext_modes[][6] = { /* In this table, element 3 (clk) controls the * clock, and gets written to 0x8700. */ /* x , y , Code, clk, n/a, pipe */ {352, 288, 0x00, 0x28, 0x00, 1023}, {320, 240, 0x01, 0x28, 0x00, 1023}, {176, 144, 0x02, 0x23, 0x00, 1023}, {160, 120, 0x03, 0x23, 0x00, 1023}, {0, 0, 0, 0, 0} }; static __u16 tv8532_ext_modes[][6] = { /* x , y , Code, clk, n/a, pipe */ {352, 288, 0x00, 0x28, 0x00, 1023}, {320, 240, 0x10, 0x28, 0x00, 1023}, {176, 144, 0x01, 0x23, 0x00, 1023}, /*{160, 120, 0x03, 0x23, 0x00, 1023}, */ {0, 0, 0, 0, 0} }; #ifdef CONFIG_PROC_FS /* Not sure what we should do with this. I think it is V4L level 2 stuff */ /* Currently only use RGB24 */ static struct palette_list plist[] = { {VIDEO_PALETTE_GREY, "GREY"}, {VIDEO_PALETTE_HI240, "HI240"}, {VIDEO_PALETTE_RGB565, "RGB565"}, {VIDEO_PALETTE_RGB24, "RGB24"}, {VIDEO_PALETTE_RGB32, "RGB32"}, {VIDEO_PALETTE_RGB555, "RGB555"}, {VIDEO_PALETTE_YUV422, "YUV422"}, {VIDEO_PALETTE_YUYV, "YUYV"}, {VIDEO_PALETTE_UYVY, "UYVY"}, {VIDEO_PALETTE_YUV420, "YUV420"}, {VIDEO_PALETTE_YUV411, "YUV411"}, {VIDEO_PALETTE_RAW, "RAW"}, {VIDEO_PALETTE_YUV422P, "YUV422P"}, {VIDEO_PALETTE_YUV411P, "YUV411P"}, {VIDEO_PALETTE_YUV420P, "YUV420P"}, {VIDEO_PALETTE_YUV410P, "YUV410P"}, {VIDEO_PALETTE_RAW_JPEG, "RJPG"}, {VIDEO_PALETTE_JPEG, "JPEG"}, {-1, NULL} }; #endif /* CONFIG_PROC_FS */ /* function for the tasklet */ void outpict_do_tasklet(unsigned long ptr); int spca50x_set_packet_size(struct usb_spca50x *spca50x, int size) { int alt; struct pwc_softc *sc = PWC_SC(spca50x); int ep = 0; /**********************************************************************/ /* Select the alternate setting for the interface depending * on the desired transfer length */ if (size == 0) /* this is always like this */ alt = SPCA50X_ALT_SIZE_0; else if (size == 128) alt = SPCA50X_ALT_SIZE_128; else if (size == 256) alt = SPCA50X_ALT_SIZE_256; else if (size == 384) alt = SPCA50X_ALT_SIZE_384; else if (size == 512) alt = SPCA50X_ALT_SIZE_512; else if (size == 640) alt = SPCA50X_ALT_SIZE_640; else if (size == 768) alt = SPCA50X_ALT_SIZE_768; else if (size == 896) alt = SPCA50X_ALT_SIZE_896; else if (size == 1000) alt = ETOMS_ALT_SIZE_1000; else if (size >= 1023) { /* XXX was == */ switch (PWC_SC(spca50x)->pwc_info.bridge) { case BRIDGE_SONIX: case BRIDGE_SN9CXXX: case BRIDGE_MR97311: case BRIDGE_PAC207: alt = 8; break; case BRIDGE_STV0602: alt = 1; /* other modes don't work */ break; case BRIDGE_EM2820: alt = 5; /* 5:... 7:3072 bytes */ break; default: alt = SPCA50X_ALT_SIZE_1023; break; } } else { /* if an unrecognised size, default to the minimum */ printf( "Set packet size: invalid size (%d), defaulting to %d\n", size, SPCA50X_ALT_SIZE_128); alt = SPCA50X_ALT_SIZE_128; } printf("%s: Set packet size: size (%d), alt becomes %d\n", __FUNCTION__, size, alt); /* check endpoint */ switch (PWC_SC(spca50x)->pwc_info.bridge) { case BRIDGE_EM2820: /* has 1, 2 and 4 */ ep = 2; break; case BRIDGE_PAC207: ep = PAC207_ENDPOINT_ADDRESS; break; default: ep = SPCA50X_ENDPOINT_ADDRESS; break; } spca50x->vendpoint = sc->vendpoint = ep; spca50x->alt = alt; #if 0 PDEBUG(5, "iface %d alt %d ep %d size %d", spca50x->iface, alt, ep, size); if (usbd_set_interface(sc->sc_iface, alt) != USBD_NORMAL_COMPLETION) { err("Set packet size: set interface error"); return -EBUSY; } { int ret; int i; uint8_t nendpt; usb_endpoint_descriptor_t *found = NULL, *edesc = NULL; int mysize = 0; ret = usbd_endpoint_count(sc->sc_iface, &nendpt); if(ret != USBD_NORMAL_COMPLETION) { device_printf(sc->sc_dev, "Failed to get endpoint count (%d)\n", ret); return -ret; } for (i = 0; i < nendpt; i++) { edesc = usbd_interface2endpoint_descriptor(sc->sc_iface, i); if (edesc) printf("desc %d endpoint %d size %d\n", i, UE_GET_ADDR(edesc->bEndpointAddress), UGETW(edesc->wMaxPacketSize)); if(edesc != NULL && UE_GET_ADDR(edesc->bEndpointAddress) == sc->vendpoint) { found = edesc; } } if(found == NULL) { device_printf(sc->sc_dev, "Failed to find videoendpoint %d\n", sc->vendpoint); return -EINVAL; } edesc = found; mysize = UGETW(edesc->wMaxPacketSize); if (mysize > 1023) mysize = 1023; PDEBUG(1, "set real packet size: %d, alt=%d", mysize, alt); } #endif return 0; } /* Returns number of bits per pixel (regardless of where they are located; planar or * not), or zero for unsupported format. */ static int spca5xx_get_depth(struct usb_spca50x *spca50x, int palette) { switch (palette) { // case VIDEO_PALETTE_GREY: return 8; case VIDEO_PALETTE_RGB565: return 16; case VIDEO_PALETTE_RGB24: return 24; // case VIDEO_PALETTE_YUV422: return 16; // case VIDEO_PALETTE_YUYV: // return 16; // case VIDEO_PALETTE_YUV420: return 24; case VIDEO_PALETTE_YUV420P: return 12; /* strange need 12 this break the read method for this planar mode (6*8/4) */ // case VIDEO_PALETTE_YUV422P: return 24; /* Planar */ case VIDEO_PALETTE_RGB32: return 32; case VIDEO_PALETTE_RAW_JPEG: return 24; /* raw jpeg. what should we return ?? */ case VIDEO_PALETTE_JPEG: if (spca50x->cameratype == JPEG || spca50x->cameratype == JPGH || spca50x->cameratype == JPGC || spca50x->cameratype == JPGS || spca50x->cameratype == JPGM) { return 8; } else return 0; default: printf("%s: invalid format %d\n", __FUNCTION__, palette); return 0; /* Invalid format */ } } /********************************************************************** * spca50x_init_isoc * Function starts the ISO USB transfer by enabling this process * from USB side and enabling ISO machine from the chip side ***********************************************************************/ void spcaCameraStart(struct usb_spca50x *spca50x) { int err_code; struct usb_device *dev = spca50x->dev; switch (PWC_SC(spca50x)->pwc_info.bridge) { default: printf("%s: unknown bridge %d (non fatal but should)\n", __FUNCTION__, PWC_SC(spca50x)->pwc_info.bridge); break; case BRIDGE_SN9CXXX: sn9cxxx_start(spca50x); break; case BRIDGE_ETOMS: Et_startCamera(spca50x); break; case BRIDGE_CX11646: cx11646_start(spca50x); break; case BRIDGE_ZC3XX: zc3xx_start(spca50x); break; case BRIDGE_SONIX: sonix_start(spca50x); break; case BRIDGE_SPCA500: err_code = spca500_initialise(spca50x); break; case BRIDGE_SPCA501: /* Enable ISO packet machine CTRL reg=2, * index=1 bitmask=0x2 (bit ordinal 1) */ err_code = spca50x_reg_write(dev, SPCA501_REG_CTLRL, 0x1, 0x2); break; case BRIDGE_SPCA504: case BRIDGE_SPCA504C: case BRIDGE_SPCA536: case BRIDGE_SPCA533: case BRIDGE_SPCA504B: sp5xxfw2_start(spca50x); break; case BRIDGE_SPCA506: spca506_start(spca50x); break; case BRIDGE_SPCA505: //necessary because without it we can see stream only once after loading module //stopping usb registers Tomasz change spca50x_reg_write(dev, 0x2, 0x0, 0x0); /* Enable ISO packet machine - should we do this here or in ISOC init ? */ err_code = spca50x_reg_write(dev, SPCA50X_REG_USB, SPCA50X_USB_CTRL, SPCA50X_CUSB_ENABLE); // spca50x_reg_write(dev, 0x5, 0x0, 0x0); // spca50x_reg_write(dev, 0x5, 0x0, 0x1); // spca50x_reg_write(dev, 0x5, 0x11, 0x2); break; case BRIDGE_SPCA508: err_code = spca50x_reg_write(dev, 0, 0x8112, 0x10 | 0x20); break; case BRIDGE_SPCA561: /* Video ISO enable, Video Drop Packet enable: */ spca561_start(spca50x); break; case BRIDGE_TV8532: tv8532_start(spca50x); break; case BRIDGE_MR97311: pcam_start(spca50x); break; case BRIDGE_PAC207: pac207_start(spca50x); break; } } static inline void spcaCameraStop2(struct usb_spca50x *spca50x) { /* stop on alt 0 */ switch (PWC_SC(spca50x)->pwc_info.bridge) { case BRIDGE_CX11646: cx11646_stop(spca50x); break; case BRIDGE_ZC3XX: zc3xx_stop(spca50x); break; } } static inline void spcaCameraStop(struct usb_spca50x *spca50x) { /* stop on alt x */ switch (PWC_SC(spca50x)->pwc_info.bridge) { case BRIDGE_SN9CXXX: sn9cxxx_stop(spca50x); break; case BRIDGE_ETOMS: Et_stopCamera(spca50x); break; case BRIDGE_CX11646: /* deferred after set_alt(0,0) cx11646_stop(spca50x); */ break; case BRIDGE_ZC3XX: //zc3xx_stop(spca50x); break; case BRIDGE_SONIX: sonix_stop(spca50x); break; case BRIDGE_SPCA500: break; case BRIDGE_SPCA501: /* Disable ISO packet machine CTRL reg=2, index=1 bitmask=0x0 (bit ordinal 1) */ spca50x_reg_write(spca50x->dev, SPCA501_REG_CTLRL, 0x1, 0x0); break; case BRIDGE_SPCA504C: case BRIDGE_SPCA504: case BRIDGE_SPCA536: case BRIDGE_SPCA533: case BRIDGE_SPCA504B: sp5xxfw2_stop(spca50x); break; case BRIDGE_SPCA505: spca50x_reg_write(spca50x->dev, 0x2, 0x0, 0x0); //Disable ISO packet machine break; case BRIDGE_SPCA506: spca506_stop(spca50x); break; case BRIDGE_SPCA508: // Video ISO disable, Video Drop Packet enable: spca50x_reg_write(spca50x->dev, 0, 0x8112, 0x20); break; case BRIDGE_SPCA561: // Video ISO disable, Video Drop Packet enable: spca561_stop(spca50x); break; case BRIDGE_TV8532: tv8532_stop(spca50x); break; case BRIDGE_MR97311: pcam_stop(spca50x); break; case BRIDGE_PAC207: pac207_stop(spca50x); break; } } static int spca50x_init_isoc(struct usb_spca50x *spca50x) { #if !defined(__FreeBSD__) struct urb *urb; int fx, err, n; int intpipe; PDEBUG(3, "*** Initializing capture ***"); /* reset iso context */ spca50x->compress = compress; spca50x->curframe = 0; spca50x->cursbuf = 0; spca50x->frame[0].seq = -1; spca50x->lastFrameRead = -1; // spca50x_set_packet_size(spca50x, spca50x->pipe_size); #endif PDEBUG(2, "setpacketsize %d", spca50x->pipe_size); #if defined(__FreeBSD__) spcaCameraStart(spca50x); #else /* !__FreeBSD__ */ for (n = 0; n < SPCA50X_NUMSBUF; n++) { #if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL); #else urb = usb_alloc_urb(FRAMES_PER_DESC); #endif if (!urb) { err("init isoc: usb_alloc_urb ret. NULL"); return -ENOMEM; } spca50x->sbuf[n].urb = urb; urb->dev = spca50x->dev; urb->context = spca50x; if (spca50x->bridge == BRIDGE_PAC207) { urb->pipe = usb_rcvisocpipe(spca50x->dev, PAC207_ENDPOINT_ADDRESS); } else { urb->pipe = usb_rcvisocpipe(spca50x->dev, SPCA50X_ENDPOINT_ADDRESS); } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) urb->transfer_flags = URB_ISO_ASAP; urb->interval = 1; #else urb->transfer_flags = USB_ISO_ASAP; #endif urb->transfer_buffer = spca50x->sbuf[n].data; urb->complete = spca50x_isoc_irq; urb->number_of_packets = FRAMES_PER_DESC; urb->transfer_buffer_length = spca50x->packet_size * FRAMES_PER_DESC; for (fx = 0; fx < FRAMES_PER_DESC; fx++) { urb->iso_frame_desc[fx].offset = spca50x->packet_size * fx; urb->iso_frame_desc[fx].length = spca50x->packet_size; } } #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,41) spca50x->sbuf[SPCA50X_NUMSBUF - 1].urb->next = spca50x->sbuf[0].urb; for (n = 0; n < SPCA50X_NUMSBUF - 1; n++) spca50x->sbuf[n].urb->next = spca50x->sbuf[n + 1].urb; #endif spcaCameraStart(spca50x); if (spca50x->bridge == BRIDGE_SONIX) { intpipe = usb_rcvintpipe(spca50x->dev, 3); usb_clear_halt(spca50x->dev, intpipe); } PDEBUG(5, "init isoc int %d altsetting %d", spca50x->iface, spca50x->alt); for (n = 0; n < SPCA50X_NUMSBUF; n++) { spca50x->sbuf[n].urb->dev = spca50x->dev; #if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,41) err = usb_submit_urb(spca50x->sbuf[n].urb, GFP_KERNEL); #else err = usb_submit_urb(spca50x->sbuf[n].urb); #endif if (err) { err("init isoc: usb_submit_urb(%d) ret %d", n, err); return err; } } for (n = 0; n < SPCA50X_NUMFRAMES; n++) { spca50x->frame[n].grabstate = FRAME_UNUSED; spca50x->frame[n].scanstate = STATE_SCANNING; } #endif /* !__FreeBSD__ */ spca50x->streaming = 1; return 0; } /********************************************************************** * spca50x_stop_isoc * Function stops the USB ISO pipe by stopping the chip ISO machine * and stopping USB transfer ***********************************************************************/ void spca50x_stop_isoc(struct usb_spca50x *spca50x) { if (!spca50x->streaming || !spca50x->dev) return; PDEBUG(3, "*** Stopping capture ***"); spcaCameraStop(spca50x); // spca50x->streaming = 0; /* XXX kill isoc */ spca50x_set_packet_size(spca50x, 0); spcaCameraStop2(spca50x); PDEBUG(3, "*** Capture stopped ***"); } /********************************************************************** * spca50x_find_mode_index * Function finds the mode index in the modes table ***********************************************************************/ static inline int spca50x_find_mode_index(int width, int height, __u16 ext_modes[][6]) { int i = 0; int j = 0; /* search through the mode list until we hit one which has * a smaller or equal resolution, or we run out of modes. */ for (i = 0; ext_modes[i][0] && (ext_modes[i][0] >= width && ext_modes[i][1] >= height); i++) { if (((ext_modes[i][2] & 0xF0) >> 4) == 0) j = (i & 0x0F) << 4; } /* search to found the first hardware available mode */ i--; /* FIXME */ PDEBUG(2, "find Hardware mode %d soft mode %d", j >> 4, i); /* check whether we found the requested mode, or ran out of * modes in the list. */ if (!ext_modes[i][0] || ext_modes[i][0] != width || ext_modes[i][1] != height) { PDEBUG(3, "Failed to find mode %d x %d", width, height); return -EINVAL; } i = (i & 0x0F) | j; PDEBUG(2, "Pass Hardware mode %d soft mode %d", (i >> 4), (i & 0x0F)); /* return the index of the requested mode */ return i; } /********************************************************************** * spca50x_smallest_mode_index * Function finds the mode index in the modes table of the smallest * available mode. ***********************************************************************/ static int spca5xx_getDefaultMode(struct usb_spca50x *spca50x) { int i; for (i = QCIF; i < TOTMODE; i++) { if (spca50x->mode_cam[i].method == 0 && spca50x->mode_cam[i].width) { spca50x->width = spca50x->mode_cam[i].width; spca50x->height = spca50x->mode_cam[i].height; spca50x->method = 0; spca50x->pipe_size = spca50x->mode_cam[i].pipe; spca50x->mode = spca50x->mode_cam[i].mode; return 0; } } return -EINVAL; } static inline int spca50x_smallest_mode_index(__u16 ext_modes[][6], int *cols, int *rows, int *pipe) { int i; int width = INT_MAX; int height = INT_MAX; int intpipe = 1023; int index = -EINVAL; /* search through the mode list until we run out of modes */ /* and take the smallest hardware mode and pipe size */ for (i = 0; ext_modes[i][0]; i++) { if (ext_modes[i][0] < width || ext_modes[i][1] < height) { if (!(ext_modes[i][2] & 0xF0)) { width = ext_modes[i][0]; height = ext_modes[i][1]; intpipe = ext_modes[i][5]; index = i; } } } /* return the index of the smallest mode */ if (index != -EINVAL) { *pipe = intpipe; if (cols != NULL) *cols = width; if (rows != NULL) *rows = height; } return i; } /********************************************************************** * spca50x_set_mode * Function sets up the resolution directly. * Attention!!! index, the index in modes array is NOT checked. ***********************************************************************/ static int spca5xx_getcapability(struct usb_spca50x *spca50x) { int maxw, maxh, minw, minh; int i; minw = minh = 255 * 255; maxw = maxh = 0; for (i = QCIF; i < TOTMODE; i++) { if (spca50x->mode_cam[i].width) { if (maxw < spca50x->mode_cam[i].width || maxh < spca50x->mode_cam[i].height) { maxw = spca50x->mode_cam[i].width; maxh = spca50x->mode_cam[i].height; } if (minw > spca50x->mode_cam[i].width || minh > spca50x->mode_cam[i].height) { minw = spca50x->mode_cam[i].width; minh = spca50x->mode_cam[i].height; } } } spca50x->maxwidth = maxw; spca50x->maxheight = maxh; spca50x->minwidth = minw; spca50x->minheight = minh; PDEBUG(0, "maxw %d maxh %d minw %d minh %d", maxw, maxh, minw, minh); return 0; } static int wxh_to_size(int width, int height) { switch (width) { case 640: if (height == 480)return VGA; break; case 384: if (height == 288)return PAL; break; case 352: if (height == 288)return CIF; break; case 320: if (height == 240)return SIF; break; case 192: if (height == 144)return QPAL; break; case 176: if (height == 144)return QCIF; break; case 160: if (height == 120)return QSIF; break; } return -EINVAL; } static int v4l_to_spca5xx(int format) { switch (format) { case VIDEO_PALETTE_RGB565: return P_RGB16; case VIDEO_PALETTE_RGB24: return P_RGB24; case VIDEO_PALETTE_RGB32: return P_RGB32; case VIDEO_PALETTE_YUV422P: return P_YUV422; case VIDEO_PALETTE_YUV420P: return P_YUV420; case VIDEO_PALETTE_RAW_JPEG: return P_RAW; case VIDEO_PALETTE_JPEG: return P_JPEG; default: printf("%s: invalid format %d\n", __FUNCTION__, format); return -EINVAL; } } int spca5xx_setMode(struct usb_spca50x *spca50x, int width, int height, int format) { int i, j; int formatIn; int crop = 0, cropx1 = 0, cropx2 = 0, cropy1 = 0, cropy2 = 0, x = 0, y = 0; /* Avoid changing to already selected mode */ /* convert V4l format to our internal format */ PDEBUG(2, "Set mode asked w %d h %d p %d", width, height, format); if ((formatIn = v4l_to_spca5xx(format)) < 0) return -EINVAL; printf("Looking for %d x %d palette 0x%x\n", width, height, formatIn); for (i = QCIF; i < TOTMODE; i++) { if ((spca50x->mode_cam[i].width == width) && (spca50x->mode_cam[i].height == height) && (spca50x->mode_cam[i].t_palette & formatIn)) { spca50x->width = spca50x->mode_cam[i].width; spca50x->height = spca50x->mode_cam[i].height; spca50x->pipe_size = spca50x->mode_cam[i].pipe; spca50x->mode = spca50x->mode_cam[i].mode; spca50x->method = spca50x->mode_cam[i].method; spca50x->format = format; //palette in use if (spca50x->method) { if (spca50x->method == 1) { for (j = i; j < TOTMODE; j++) { if (spca50x->mode_cam[j].method == 0 && spca50x->mode_cam[j].width) { spca50x->hdrwidth = spca50x->mode_cam[j].width; spca50x->hdrheight = spca50x->mode_cam[j].height; spca50x->mode = spca50x->mode_cam[j].mode; // overwrite by the hardware mode break; } } // end match hardware mode if (!spca50x->hdrwidth && !spca50x->hdrheight) return -EINVAL; } } /* match found */ break; } } // end match mode /* initialize the hdrwidth and hdrheight for the first init_source */ /* precompute the crop x y value for each frame */ if (!spca50x->method) { /* nothing todo hardware found stream */ cropx1 = cropx2 = cropy1 = cropy2 = x = y = 0; spca50x->hdrwidth = spca50x->width; spca50x->hdrheight = spca50x->height; } if (spca50x->method & 0x01) { /* cropping method */ if (spca50x->hdrwidth > spca50x->width) { crop = (spca50x->hdrwidth - spca50x->width); if (spca50x->cameratype == JPEG || spca50x->cameratype == JPGH || spca50x->cameratype == JPGS) crop = crop >> 4; cropx1 = crop >> 1; cropx2 = cropx1 + (crop % 2); } else { cropx1 = cropx2 = 0; } if (spca50x->hdrheight > spca50x->height) { crop = (spca50x->hdrheight - spca50x->height); if (spca50x->cameratype == JPEG) crop = crop >> 4; if (spca50x->cameratype == JPGH || spca50x->cameratype == JPGS) crop = crop >> 3; cropy1 = crop >> 1; cropy2 = cropy1 + (crop % 2); } else { cropy1 = cropy2 = 0; } } if (spca50x->method & 0x02) { /* what can put here for div method */ } if (spca50x->method & 0x04) { /* and here for mult */ } PDEBUG(2, "Found code %d method %d", spca50x->mode, spca50x->method); PDEBUG(2, "Soft Win width height %d x %d", spca50x->width, spca50x->height); PDEBUG(2, "Hard Win width height %d x %d", spca50x->hdrwidth, spca50x->hdrheight); #if !defined(__FreeBSD__) for (i = 0; i < SPCA50X_NUMFRAMES; i++) { spca50x->frame[i].method = spca50x->method; spca50x->frame[i].cameratype = spca50x->cameratype; spca50x->frame[i].cropx1 = cropx1; spca50x->frame[i].cropx2 = cropx2; spca50x->frame[i].cropy1 = cropy1; spca50x->frame[i].cropy2 = cropy2; spca50x->frame[i].x = x; spca50x->frame[i].y = y; spca50x->frame[i].hdrwidth = spca50x->hdrwidth; spca50x->frame[i].hdrheight = spca50x->hdrheight; spca50x->frame[i].width = spca50x->width; spca50x->frame[i].height = spca50x->height; spca50x->frame[i].format = spca50x->format; spca50x->frame[i].scanlength = spca50x->width * spca50x->height * 3 / 2; // ?? assumes 4:2:0 data } #endif /* !__FreeBSD__ */ return 0; } static inline int spca50x_set_mode(struct usb_spca50x *spca50x, int index, __u16 ext_modes[][6]) { struct usb_device *dev = spca50x->dev; int i; int mode = 0; int method = 0; int crop = 0, cropx1 = 0, cropx2 = 0, cropy1 = 0, cropy2 = 0, x = 0, y = 0; switch (PWC_SC(spca50x)->pwc_info.bridge) { case BRIDGE_ETOMS: spca50x->mode = (ext_modes[index][2] & 0x0f); break; case BRIDGE_TV8532: mode = (ext_modes[index][2]) & 0x0F; method = (ext_modes[index][2] & 0xF0) >> 4; spca50x->mode = mode; break; case BRIDGE_CX11646: mode = (ext_modes[index][2]) & 0x0F; method = (ext_modes[index][2] & 0xF0) >> 4; spca50x->mode = mode; break; #if 0 case BRIDGE_ZC3XX: mode = (ext_modes[index][2]) & 0x0F; method = (ext_modes[index][2] & 0xF0) >> 4; spca50x->mode = mode; break; case BRIDGE_SONIX: mode = (ext_modes[index][2] & 0x0f); spca50x->mode = mode; break; case BRIDGE_SN9CXXX: mode = (ext_modes[index][2]) & 0x0F; method = (ext_modes[index][2] & 0xF0) >> 4; spca50x->mode = mode; break; #endif case BRIDGE_SPCA506: spca506_Setsize(spca50x, ext_modes[index][2], ext_modes[index][3], ext_modes[index][4]); break; case BRIDGE_SPCA505: spca50x_reg_write(dev, SPCA50X_REG_COMPRESS, 0x0, ext_modes[index][2]); spca50x_reg_write(dev, SPCA50X_REG_COMPRESS, 0x6, ext_modes[index][3]); spca50x_reg_write(dev, SPCA50X_REG_COMPRESS, 0x7, ext_modes[index][4]); break; case BRIDGE_SPCA501: mode = (ext_modes[index][2]) & 0x0F; method = (ext_modes[index][2] & 0xF0) >> 4; spca50x_reg_write(dev, SPCA50X_REG_USB, 0x6, ext_modes[index][3]); spca50x_reg_write(dev, SPCA50X_REG_USB, 0x7, ext_modes[index][4]); break; case BRIDGE_SPCA508: spca50x_reg_write(dev, 0, 0x8500, ext_modes[index][2]); // mode spca50x_reg_write(dev, 0, 0x8700, ext_modes[index][3]); // clock break; #if 0 case BRIDGE_SPCA561: spca50x->mode = (ext_modes[index][2]) & 0x0F; method = (ext_modes[index][2] & 0xF0) >> 4; //spca50x_reg_write (dev, 0, 0x8500, mode); // mode //spca50x_reg_write (dev, 0, 0x8700, ext_modes[index][3]); // clock breal; #endif case BRIDGE_SPCA500: mode = (ext_modes[index][2]) & 0x0F; method = (ext_modes[index][2] & 0xF0) >> 4; x = ext_modes[index][3] & 0x0F; y = ext_modes[index][4] & 0x0F; /* Experimental Wait until Marek Test // set x multiplier spca50x_reg_write (dev, 0, 0x8001,ext_modes[index][3]); // set y multiplier spca50x_reg_write (dev, 0, 0x8002,ext_modes[index][4]); // use compressed mode, VGA, with mode specific subsample spca50x_reg_write (dev, 0, 0x8003,((ext_modes[index][2] & 0x0f) << 4)); */ break; #if 0 case BRIDGE_SPCA504: mode = (ext_modes[index][2] + 3) & 0x0F; method = (ext_modes[index][2] & 0xF0) >> 4; x = ext_modes[index][3] & 0x0F; y = ext_modes[index][4] & 0x0F; if (spca50x->desc == AiptekMiniPenCam13) { /* spca504a aiptek */ spca504A_acknowledged_command(spca50x, 0x8, mode, 0, (0x80 | (mode & 0x0F)), 1); spca504A_acknowledged_command(spca50x, 1, 3, 0, 0x9F, 0); } else { spca504_acknowledged_command(spca50x, 0x8, mode, 0); } break; case BRIDGE_SPCA504C: spca50x_reg_write(spca50x->dev, 0xa0, (0x0500 | (ext_modes[index][2] & 0x0F)), 0x0); // capture mode spca50x_reg_write(spca50x->dev, 0x20, 0x1, (0x0500 | (ext_modes[index][2] & 0x0F))); // snapshot mode break; case BRIDGE_SPCA504B: mode = ext_modes[index][2] & 0x0F; method = (ext_modes[index][2] & 0xF0) >> 4; x = ext_modes[index][3] & 0x0F; y = ext_modes[index][4] & 0x0F; spca504B_SetSizeType(spca50x, mode, 6); break; case BRIDGE_SPCA533: mode = ext_modes[index][2] & 0x0F; method = (ext_modes[index][2] & 0xF0) >> 4; x = ext_modes[index][3] & 0x0F; y = ext_modes[index][4] & 0x0F; if ((spca50x->desc == MegapixV4) || (spca50x->desc == LogitechClickSmart820)) { // megapix specific stuff spca533_Megapix(spca50x); } else { spca504B_SetSizeType(spca50x, mode, 6); } break; case BRIDGE_SPCA536: mode = ext_modes[index][2] & 0x0F; method = (ext_modes[index][2] & 0xF0) >> 4; spca536_SetSizeType(spca50x, mode, 6); break; #endif } spca50x->pipe_size = ext_modes[index][5]; spca50x->method = method; /* initialize the hdrwidth and hdrheight for the first init_source */ if (!(spca50x->hdrwidth) || !(spca50x->hdrheight)) { spca50x->hdrwidth = spca50x->width; spca50x->hdrheight = spca50x->height; } /* precompute the crop x y value for each frame */ if (!method) { /* nothing todo hardware found stream */ cropx1 = cropx2 = cropy1 = cropy2 = x = y = 0; } if (method & 0x01) { /* cropping method */ if (spca50x->hdrwidth > spca50x->width) { crop = (spca50x->hdrwidth - spca50x->width); if (spca50x->cameratype == JPEG || spca50x->cameratype == JPGH || spca50x->cameratype == JPGS) crop = crop >> 4; cropx1 = crop >> 1; cropx2 = cropx1 + (crop % 2); } else { cropx1 = cropx2 = 0; } if (spca50x->hdrheight > spca50x->height) { crop = (spca50x->hdrheight - spca50x->height); if (spca50x->cameratype == JPEG) crop = crop >> 4; if (spca50x->cameratype == JPGH || spca50x->cameratype == JPGS) crop = crop >> 3; cropy1 = crop >> 1; cropy2 = cropy1 + (crop % 2); } else { cropy1 = cropy2 = 0; } } if (method & 0x02) { /* what can put here for div method */ } if (method & 0x04) { /* and here for mult */ } PDEBUG(2, "Found code %d method %d", mode, method); PDEBUG(2, "Soft Win width height %d x %d", spca50x->width, spca50x->height); PDEBUG(2, "Hard Win width height %d x %d", spca50x->hdrwidth, spca50x->hdrheight); i = 0; /* silence compiler */ #if !defined(__FreeBSD__) /* XXX copied from above */ for (i = 0; i < SPCA50X_NUMFRAMES; i++) { spca50x->frame[i].method = spca50x->method; spca50x->frame[i].cameratype = spca50x->cameratype; spca50x->frame[i].cropx1 = cropx1; spca50x->frame[i].cropx2 = cropx2; spca50x->frame[i].cropy1 = cropy1; spca50x->frame[i].cropy2 = cropy2; spca50x->frame[i].x = x; spca50x->frame[i].y = y; spca50x->frame[i].hdrwidth = spca50x->hdrwidth; spca50x->frame[i].hdrheight = spca50x->hdrheight; spca50x->frame[i].width = spca50x->width; spca50x->frame[i].height = spca50x->height; spca50x->frame[i].scanlength = spca50x->width * spca50x->height * 3 / 2; // ?? assumes 4:2:0 data } #endif /* !__FreeBSD__ */ return 0; } /********************************************************************** * spca50x_mode_init_regs * Function sets up the resolution with checking if it's necessary ***********************************************************************/ static int spca5xx_restartMode(struct usb_spca50x *spca50x, int width, int height, int format) { int was_streaming; int r; /* Avoid changing to already selected mode */ if (spca50x->width == width && spca50x->height == height) { PDEBUG(1, "*** no need to change mode\n"); // return 0; } PDEBUG(1, "Mode changing to %d,%d", width, height); was_streaming = spca50x->streaming; /* FIXME spca500 bridge is there a way to find an init like Clicksmart310 ? */ if (was_streaming) { if ((PWC_SC(spca50x)->pwc_info.bridge != BRIDGE_SPCA500) || (spca50x->desc == LogitechClickSmart310)) spca50x_stop_isoc(spca50x); } PDEBUG(2, "Set mode asked w %d h %d p %d", width, height, format); r = spca5xx_setMode(spca50x, width, height, format); if (r < 0) goto out; if (was_streaming) { if ((PWC_SC(spca50x)->pwc_info.bridge != BRIDGE_SPCA500) || (spca50x->desc == LogitechClickSmart310)) { r = spca50x_init_isoc(spca50x); } else { spcaCameraStart(spca50x); } } out: return r; } static int spca50x_mode_init_regs(struct usb_spca50x *spca50x, int width, int height, int mode, __u16 ext_modes[][6]) { int i, j; int r; int was_streaming; /* Avoid changing to already selected mode */ if (spca50x->width == width && spca50x->height == height) return 0; /* find a suitable mode */ if ((i = spca50x_find_mode_index(width, height, ext_modes)) == -EINVAL) return -EINVAL; /* keep the hardware index from the hight nibble */ j = (i & 0xF0) >> 4; /* the software too */ i = i & 0x0F; /* make sure that the suitable mode isn't the current mode */ if (spca50x->width == ext_modes[i][0] && spca50x->height == ext_modes[i][1]) return 0; PDEBUG(1, "Mode changing to %d,%d", width, height); was_streaming = spca50x->streaming; /* FIXME spca500 bridge is there a way to find an init like Clicksmart310 ? */ if (was_streaming) { if ((PWC_SC(spca50x)->pwc_info.bridge != BRIDGE_SPCA500) || (spca50x->desc == LogitechClickSmart310)) spca50x_stop_isoc(spca50x); } spca50x->hdrwidth = ext_modes[j][0]; spca50x->hdrheight = ext_modes[j][1]; spca50x->width = ext_modes[i][0]; spca50x->height = ext_modes[i][1]; spca50x_set_mode(spca50x, i, ext_modes); r = 0; if (was_streaming) { if ((PWC_SC(spca50x)->pwc_info.bridge != BRIDGE_SPCA500) || (spca50x->desc == LogitechClickSmart310)) { r = spca50x_init_isoc(spca50x); } else { spcaCameraStart(spca50x); } } return r; } /********************************************************************** * spca50x_get_brightness * Function reads the brightness from the camera * Receives the pointer to the description structure * returns the value of brightness **********************************************************************/ __u16 spca50x_get_brightness(struct usb_spca50x *spca50x) { __u16 brightness = 0; // value of the brightness switch (PWC_SC(spca50x)->pwc_info.bridge) { case BRIDGE_PAC207: brightness = pac207_getbrightness(spca50x); break; case BRIDGE_SN9CXXX: brightness = sn9cxxx_getbrightness(spca50x); break; case BRIDGE_ETOMS: brightness = Et_getbrightness(spca50x); break; case BRIDGE_ZC3XX: brightness = zc3xx_getbrightness(spca50x); break; case BRIDGE_SONIX: brightness = sonix_getbrightness(spca50x); break; case BRIDGE_TV8532: brightness = tv8532_getbrightness(spca50x); break; case BRIDGE_CX11646: brightness = cx_getbrightness(spca50x); break; case BRIDGE_SPCA500: { /* brightness is stored by the bridge as * 129 -> 128, with 0 being the midpoint. * v4l always keeps these values unsigned * 16 bit, that is 0 to 65535. * scale accordingly. */ brightness = spca50x_reg_read(spca50x->dev, 0x00, 0x8167, 1); brightness = brightness << 8; break; } case BRIDGE_SPCA505: //here505b brightness = 65535 - ((spca50x_reg_read(spca50x->dev, 5, 0x01, 1) >> 2) + (spca50x_reg_read(spca50x->dev, 5, 0x0, 1) << 6)); break; case BRIDGE_SPCA506: brightness = spca50x_read_i2c(spca50x, SAA7113_I2C_BASE_READ, 0xa); break; case BRIDGE_SPCA501: { brightness = spca50x_reg_read(spca50x->dev, 0x0, 0x00, 2) & 0xFF; brightness -= 125; brightness <<= 1; break; } case BRIDGE_SPCA536: case BRIDGE_SPCA533: case BRIDGE_SPCA504: case BRIDGE_SPCA504B: case BRIDGE_SPCA504C: brightness = sp5xxfw2_getbrightness(spca50x); case BRIDGE_SPCA561: break; #ifdef SPCA50X_ENABLE_EXP_BRIGHTNESS case BRIDGE_SPCA508: brightness = spca50x_reg_read(spca50x->dev, 0, 0x8651, 1); brightness = brightness << 8; break; #endif /* SPCA50X_ENABLE_EXP_BRIGHTNESS */ case BRIDGE_MR97311: brightness = 0x14; break; default: brightness = 0; break; } return brightness; } /********************************************************************** * spca50x_set_brightness * Function sets the brightness to the camera * Receives the pointer to the description structure * and brightness value **********************************************************************/ void spca50x_set_brightness(struct usb_spca50x *spca50x, __u8 brightness) { switch (PWC_SC(spca50x)->pwc_info.bridge) { case BRIDGE_PAC207: spca50x->brightness = brightness << 8; pac207_setbrightness(spca50x); break; case BRIDGE_SN9CXXX: spca50x->brightness = brightness << 8; sn9cxxx_setbrightness(spca50x); break; case BRIDGE_ETOMS: spca50x->brightness = brightness << 8; Et_setbrightness(spca50x); break; case BRIDGE_ZC3XX: spca50x->brightness = brightness << 8; zc3xx_setbrightness(spca50x); break; case BRIDGE_SONIX: spca50x->brightness = brightness << 8; sonix_setbrightness(spca50x); break; case BRIDGE_TV8532: spca50x->brightness = brightness << 8; tv8532_setbrightness(spca50x); break; case BRIDGE_CX11646: spca50x->brightness = brightness << 8; cx_setbrightness(spca50x); break; case BRIDGE_SPCA500: /* 0 - 255 with zero at 128 */ spca50x_reg_write(spca50x->dev, 0x00, 0x8167, brightness); break; case BRIDGE_SPCA505: //here505b // brightness spca50x_reg_write(spca50x->dev, 5, 0x0, (255 - brightness) >> 6); spca50x_reg_write(spca50x->dev, 5, 0x01, (255 - brightness) << 2); break; case BRIDGE_SPCA506: spca50x_write_i2c(spca50x, SAA7113_I2C_BASE_WRITE, 0xa, brightness); break; case BRIDGE_SPCA501: spca50x_reg_write(spca50x->dev, 0x0, 0x00, (brightness >> 1) + 125); break; case BRIDGE_SPCA536: case BRIDGE_SPCA533: case BRIDGE_SPCA504: case BRIDGE_SPCA504B: case BRIDGE_SPCA504C: spca50x->brightness = brightness << 8; sp5xxfw2_setbrightness(spca50x); break; case BRIDGE_SPCA561: break; #ifdef SPCA50X_ENABLE_EXP_BRIGHTNESS case BRIDGE_SPCA508: spca50x_reg_write(spca50x->dev, 0, 0x8651, brightness); spca50x_reg_write(spca50x->dev, 0, 0x8652, brightness); spca50x_reg_write(spca50x->dev, 0, 0x8653, brightness); spca50x_reg_write(spca50x->dev, 0, 0x8654, brightness); /* autoadjust is set to 0 to avoid doing repeated brightness calculation on the same frame. autoadjust is set to 0 when a new frame is ready. */ autoadjust = 0; break; #endif /* SPCA50X_ENABLE_EXP_BRIGHTNESS */ case BRIDGE_MR97311: break; default: break; } } #if !defined(__FreeBSD__) /* XXX probably not fully necessary */ /********************************************************************** * * SPCA50X data transfer, IRQ handler * **********************************************************************/ static struct spca50x_frame *spca50x_next_frame(struct usb_spca50x *spca50x, unsigned char *cdata) { int iFrameNext; struct spca50x_frame *frame = NULL; PDEBUG(2, "Frame %d State %d", spca50x->curframe, spca50x->frame[spca50x->curframe].grabstate); if (spca50x->frame[spca50x->curframe].grabstate == FRAME_ERROR) { PDEBUG(2, "Frame %d errdrop", spca50x->curframe); frame = &spca50x->frame[spca50x->curframe]; goto dropframe; } /* Cycle through the frame buffer looking for a free frame to overwrite */ iFrameNext = (spca50x->curframe + 1) % SPCA50X_NUMFRAMES; while (frame == NULL && iFrameNext != (spca50x->curframe)) { if (spca50x->frame[iFrameNext].grabstate == FRAME_READY || spca50x->frame[iFrameNext].grabstate == FRAME_UNUSED || spca50x->frame[iFrameNext].grabstate == FRAME_ERROR) { spca50x->curframe = iFrameNext; frame = &spca50x->frame[iFrameNext]; break; } else { iFrameNext = (iFrameNext + 1) % SPCA50X_NUMFRAMES; } } if (frame == NULL) { PDEBUG(3, "Can't find a free frame to grab into...using next. " "This is caused by the application not reading fast enough."); spca50x->curframe = (spca50x->curframe + 1) % SPCA50X_NUMFRAMES; frame = &spca50x->frame[spca50x->curframe]; } dropframe: frame->grabstate = FRAME_GRABBING; /* Record the frame sequence number the camera has told us */ if (cdata) { switch (spca50x->bridge) { case BRIDGE_SPCA500: frame->seq = cdata[SPCA500_OFFSET_FRAMSEQ]; break; case BRIDGE_SPCA501: frame->seq = cdata[SPCA501_OFFSET_FRAMSEQ]; break; case BRIDGE_SPCA504: frame->seq = cdata[SPCA50X_OFFSET_FRAMSEQ]; break; case BRIDGE_SPCA504C: frame->seq = cdata[SPCA50X_OFFSET_FRAMSEQ]; break; case BRIDGE_SPCA504B: frame->seq = cdata[SPCA50X_OFFSET_FRAMSEQ]; break; case BRIDGE_SPCA505: frame->seq = cdata[SPCA50X_OFFSET_FRAMSEQ]; break; case BRIDGE_SPCA506: frame->seq = cdata[SPCA50X_OFFSET_FRAMSEQ]; break; case BRIDGE_SPCA508: frame->seq = cdata[SPCA508_OFFSET_FRAMSEQ]; break; case BRIDGE_SPCA533: frame->seq = cdata[SPCA533_OFFSET_FRAMSEQ]; break; case BRIDGE_SPCA561: frame->seq = cdata[SPCA561_OFFSET_FRAMSEQ]; break; case BRIDGE_SPCA536: frame->seq = cdata[SPCA536_OFFSET_FRAMSEQ]; break; case BRIDGE_CX11646: case BRIDGE_SONIX: case BRIDGE_ZC3XX: case BRIDGE_TV8532: case BRIDGE_ETOMS: case BRIDGE_SN9CXXX: case BRIDGE_MR97311: case BRIDGE_PAC207: frame->seq = 00; break; } } if (spca50x->pictsetting.change) { memcpy(&frame->pictsetting, &spca50x->pictsetting, sizeof(struct pictparam)); /* reset flag change */ spca50x->pictsetting.change = 0; PDEBUG(2, "Picture setting change Pass to decoding "); } /* Reset some per-frame variables */ frame->highwater = frame->data; frame->scanstate = STATE_LINES; frame->scanlength = 0; frame->last_packet = -1; frame->totlength = 0; spca50x->packet = 0; return frame; } /* Tasklet function to decode */ void outpict_do_tasklet(unsigned long ptr) { int err; struct spca50x_frame *taskletframe = (struct spca50x_frame *) ptr; taskletframe->scanlength = taskletframe->highwater - taskletframe->data; PDEBUG(2, "Tasklet ask spcadecoder hdrwidth %d hdrheight %d method %d ", taskletframe->hdrwidth, taskletframe->hdrheight, taskletframe->method); if ((err = spca50x_outpicture(taskletframe)) < 0) { PDEBUG(2, "frame decoder failed (%d)", err); taskletframe->grabstate = FRAME_ERROR; } else { taskletframe->grabstate = FRAME_DONE; PDEBUG(2, "Decode framestate return %d", taskletframe->grabstate); } if (waitqueue_active(&taskletframe->wq)) wake_up_interruptible(&taskletframe->wq); } #endif /* !__FreeBSD__ */ /********************************************************************* time Helper function **********************************************************************/ static inline unsigned long spca5xx_gettimes(void) { #if defined(__FreeBSD__) struct timeval tv; getmicrotime(&tv); return (tv.tv_sec * 1000 + tv.tv_usec / 1000); #elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0) struct timeval tv; do_gettimeofday(&tv); return (tv.tv_sec * 1000 + tv.tv_usec / 1000); #else u64 times_now; times_now = get_jiffies_64(); return jiffies_to_msecs(times_now); #endif } #if defined(__FreeBSD__) #include "pwc.h" /* * determines whether this is a good frame, etc */ int _unused_spca50x_move_data_helper(struct usb_spca50x *spca50x, unsigned char *cdata, int datalength) { int iPix; //Offset of pixel data in the ISO packet unsigned long ms_times_now; unsigned long ms_times_before; int totlen = 0; int tv8532 = 0; int j, p; int sequenceNumber; int seqframe = 0; int sof; unsigned char *pData; int __once = 1; struct pwc_frame_buf *frame = PWC_SC(spca50x)->fill_frame; frame->highwater = frame->data + frame->filled; while (__once-- > 0) { /* to minimize diffs since we enter in a block */ #else /* ****************************************************************** * spca50x_move_data * Function serves for moving data from USB transfer buffers * to internal driver frame buffers. ******************************************************************* */ static int spca50x_move_data(struct usb_spca50x *spca50x, struct urb *urb) { unsigned char *cdata; //Pointer to buffer where we do store next packet unsigned char *pData; //Pointer to buffer where we do store next packet int i; struct spca50x_frame *frame; //Pointer to frame data int iPix; //Offset of pixel data in the ISO packet unsigned long ms_times_now; unsigned long ms_times_before; int totlen = 0; int tv8532 = 0; for (i = 0; i < urb->number_of_packets; i++) { int datalength = urb->iso_frame_desc[i].actual_length; int st = urb->iso_frame_desc[i].status; int sequenceNumber; int seqframe = 0; int sof; int j, p; /* PDEBUG(5,"Packet data [%d,%d,%d]", datalength, st, urb->iso_frame_desc[i].offset); */ urb->iso_frame_desc[i].actual_length = 0; urb->iso_frame_desc[i].status = 0; cdata = ((unsigned char *) urb->transfer_buffer) + urb->iso_frame_desc[i].offset; /* Check for zero length block or no selected frame buffer */ if (!datalength || spca50x->curframe == -1) { tv8532 = 0; continue; } PDEBUG(5, "Packet data [%d,%d,%d] Status: %d", datalength, st, urb->iso_frame_desc[i].offset, st); if (st) PDEBUG(2, "data error: [%d] len=%d, status=%d", i, datalength, st); frame = &spca50x->frame[spca50x->curframe]; #endif /* !__FreeBSD__ */ totlen = frame->totlength; /* read the sequence number */ switch (PWC_SC(spca50x)->pwc_info.bridge) { case BRIDGE_SONIX: case BRIDGE_ZC3XX: case BRIDGE_CX11646: case BRIDGE_TV8532: case BRIDGE_ETOMS: case BRIDGE_SN9CXXX: case BRIDGE_MR97311: case BRIDGE_PAC207: if (frame->last_packet == -1) { /*initialize a new frame */ sequenceNumber = 0; } else { sequenceNumber = frame->last_packet; } break; default: sequenceNumber = cdata[SPCA50X_OFFSET_SEQUENCE]; PDEBUG(4, "Packet start %x %x %x %x %x", cdata[0], cdata[1], cdata[2], cdata[3], cdata[4]); } /* check frame start */ switch (PWC_SC(spca50x)->pwc_info.bridge) { case BRIDGE_SN9CXXX: iPix = 0; sof = datalength - 64; if (sof < 0) sequenceNumber++; else if (cdata[sof] == 0xff && cdata[sof + 1] == 0xd9) { sequenceNumber = 0; //start of frame // copy the end of data frame memcpy(frame->highwater, cdata, sof + 2); frame->highwater += (sof + 2); totlen += (sof + 2); #if 0 spin_lock(&spca50x->v4l_lock); spca50x->avg_lum = (cdata[sof + 24] + cdata[sof + 26]) >> 1; spin_unlock(&spca50x->v4l_lock); PDEBUG(5, "mean luma %d", spca50x->avg_lum); #endif PDEBUG(5, "Sonix header packet found datalength %d totlength %d!!", datalength, totlen); PDEBUG(5, "%03d %03d %03d %03d %03d %03d %03d %03d", cdata[sof + 24], cdata[sof + 25], cdata[sof + 26], cdata[sof + 27], cdata[sof + 28], cdata[sof + 29], cdata[sof + 30], cdata[sof + 31]); // setting to skip the rest of the packet spca50x->header_len = datalength; } else sequenceNumber++; break; case BRIDGE_ETOMS: iPix = 8; seqframe = cdata[0] & 0x3f; datalength = (int) (((cdata[0] & 0xc0) << 2) | cdata[1]); if (seqframe == 0x3f) { PDEBUG(5, "Etoms header packet found datalength %d totlength %d!!", datalength, totlen); PDEBUG(5, "Etoms G %d R %d G %d B %d", cdata[2], cdata[3], cdata[4], cdata[5]); sequenceNumber = 0; //start of frame spca50x->header_len = 30; } else { if (datalength) { sequenceNumber++; } else { /* Drop Packet */ continue; } } break; case BRIDGE_CX11646: iPix = 0; if (cdata[0] == 0xFF && cdata[1] == 0xD8) { sequenceNumber = 0; spca50x->header_len = 2; PDEBUG(5, "Cx11646 header packet found datalength %d totlength %d!!", datalength, totlen); } else { sequenceNumber++; } break; case BRIDGE_ZC3XX: iPix = 0; if (cdata[0] == 0xFF && cdata[1] == 0xD8) { sequenceNumber = 0; spca50x->header_len = 2; //18 remove 0xff 0xd8; PDEBUG(5, "Zc301 header packet found datalength %d totlength %d!!", datalength, totlen); } else { sequenceNumber++; } break; case BRIDGE_SONIX: iPix = 0; sof = 0; j = 0; p = 0; if (datalength < 24) { if (datalength > 6) { j = datalength - 6; } else { j = 0; } if (j > 0) { for (p = 0; p < j; p++) { if ((cdata[0 + p] == 0xFF) && (cdata[1 + p] == 0xFF) && (cdata[2 + p] == 0x00) && (cdata[3 + p] == 0xC4) && (cdata[4 + p] == 0xC4) && (cdata[5 + p] == 0x96)) { sof = 1; break; } } } } if (sof) { sequenceNumber = 0; spca50x->header_len = p + 12; PDEBUG(5, "Sonix header packet found %d datalength %d totlength %d!!", p, datalength, totlen); } else { /* drop packet */ sequenceNumber++; } break; case BRIDGE_SPCA500: case BRIDGE_SPCA533: iPix = 1; if (sequenceNumber == SPCA50X_SEQUENCE_DROP) { if (cdata[1] == 0x01) { sequenceNumber = 0; } else { /* drop packet */ PDEBUG(5, "Dropped packet (expected seq 0x%02x)", frame->last_packet + 1); continue; } } else { sequenceNumber++; } break; case BRIDGE_SPCA536: iPix = 2; if (sequenceNumber == SPCA50X_SEQUENCE_DROP) { sequenceNumber = 0; } else { sequenceNumber++; } break; case BRIDGE_SPCA504: case BRIDGE_SPCA504B: case BRIDGE_SPCA504C: iPix = 1; switch (sequenceNumber) { case 0xfe: sequenceNumber = 0; break; case SPCA50X_SEQUENCE_DROP: /* drop packet */ PDEBUG(5, "Dropped packet (expected seq 0x%02x)", frame->last_packet + 1); continue; default: sequenceNumber++; break; } break; case BRIDGE_TV8532: //iPix = 4; iPix = 0; spca50x->header_len = 0; PDEBUG(1, "icm532, sequenceNumber: 0x%02x packet %d ", sequenceNumber, spca50x->packet); /* here we detect 0x80 */ if (cdata[0] == 0x80) { /* counter is limited so we need few header for a frame :) */ /* header 0x80 0x80 0x80 0x80 0x80 */ /* packet 00 63 127 145 00 */ /* sof 0 1 1 0 0 */ /* update sequence */ if ((spca50x->packet == 63) || (spca50x->packet == 127)) tv8532 = 1; /* is there a frame start ? */ if (spca50x->packet >= ((spca50x->hdrheight >> 1) - 1)) { PDEBUG(1, "SOF > %d seqnumber %d packet %d", tv8532, sequenceNumber, spca50x->packet); if (!tv8532) { sequenceNumber = 0; spca50x->packet = 0; } } else { if (!tv8532) { // Drop packet frame corrupt PDEBUG(1, "DROP SOF %d seqnumber %d packet %d", tv8532, sequenceNumber, spca50x->packet); frame->last_packet = sequenceNumber = 0; spca50x->packet = 0; continue; } tv8532 = 1; sequenceNumber++; spca50x->packet++; } } else { sequenceNumber++; spca50x->packet++; } break; case BRIDGE_MR97311: iPix = 0; sof = 0; j = 0; p = 0; if (datalength < 6) continue; else { for (p = 0; p < datalength - 6; p++) { if ((cdata[0 + p] == 0xFF) && (cdata[1 + p] == 0xFF) && (cdata[2 + p] == 0x00) && (cdata[3 + p] == 0xFF) && (cdata[4 + p] == 0x96) ) { if ((cdata[5 + p] == 0x64) || (cdata[5 + p] == 0x65) || (cdata[5 + p] == 0x66) || (cdata[5 + p] == 0x67)) { sof = 1; break; } } } if (sof) { sequenceNumber = 0; spca50x->header_len = p + 16; PDEBUG(5, "Pcam header packet found, %d datalength %d totlength %d!!", p, datalength, totlen); } else { /* drop packet */ sequenceNumber++; } } break; case BRIDGE_PAC207: iPix = 0; sof = 0; j = 0; p = 0; if (datalength < 6) continue; else { for (p = 0; p < datalength - 6; p++) { if ((cdata[0 + p] == 0xFF) && (cdata[1 + p] == 0xFF) && (cdata[2 + p] == 0x00) && (cdata[3 + p] == 0xFF) && (cdata[4 + p] == 0x96) ) { sof = 1; break; } } if (sof) { sequenceNumber = 0; #if 1 spin_lock(&spca50x->v4l_lock); spca50x->avg_lum = cdata[p+9] ; spin_unlock(&spca50x->v4l_lock); PDEBUG(5, "mean luma %d", spca50x->avg_lum); #endif // copy the end of data to the current frame memcpy(frame->highwater, cdata, p); frame->highwater += p; totlen += p; spca50x->header_len = p; //copy to the nextframe start at p PDEBUG(5, "Pixartcam header packet found, %d datalength %d totlength %d!!", p, datalength, totlen); } else { /* drop packet */ sequenceNumber++; } } break; case BRIDGE_SPCA561: iPix = 1; if (sequenceNumber == SPCA50X_SEQUENCE_DROP) { PDEBUG(3, "Dropped packet (expected seq 0x%02x)", frame->last_packet + 1); continue; } #if 0 if (!sequenceNumber) { spin_lock(&spca50x->v4l_lock); spca50x->avg_lum = (75 * (cdata[11] + cdata[14]) + 77 * cdata[12] + 29 * cdata[13]) >> 8; spin_unlock(&spca50x->v4l_lock); PDEBUG(4, "Frame %d,Win2 %02d ", cdata[4], spca50x->avg_lum); } #endif break; default: iPix = 1; /* check if this is a drop packet */ if (sequenceNumber == SPCA50X_SEQUENCE_DROP) { PDEBUG(3, "Dropped packet (expected seq 0x%02x)", frame->last_packet + 1); continue; } break; } if(0) PDEBUG(3, "spca50x: Packet seqnum = 0x%02x. curframe=%2d", sequenceNumber, spca50x->curframe); pData = cdata; /* Can we find a frame start */ if (sequenceNumber == 0) { totlen = 0; iPix = spca50x->header_len; PDEBUG(3, "spca50x: Found Frame Start!, framenum = %d", spca50x->curframe); // Start of frame is implicit end of previous frame // Check for a previous frame and finish it off if one exists if (frame->scanstate == STATE_LINES) { if (frame->format != VIDEO_PALETTE_RAW_JPEG) { ms_times_now = spca5xx_gettimes(); /* overflow ? */ if (ms_times_now < spca50x->last_times) spca50x->last_times = 0; spin_lock(&spca50x->v4l_lock); ms_times_before = spca50x->last_times + spca50x->dtimes; spin_unlock(&spca50x->v4l_lock); if (ms_times_now >= ms_times_before) { PDEBUG(2, "Decode frame last %d", (int) (ms_times_now - spca50x->last_times)); spca50x->last_times = ms_times_now; /* Decode the frame */ #if defined(__FreeBSD__) // outpict_do_tasklet((unsigned long) frame); #else /* !__FreeBSD__ */ tasklet_init(&spca50x->spca5xx_tasklet, outpict_do_tasklet, (unsigned long) frame); tasklet_schedule(&spca50x->spca5xx_tasklet); #endif /* !__FreeBSD__ */ } else frame->grabstate = FRAME_ERROR; } else { /* RAW DATA stream */ frame->grabstate = FRAME_DONE; if (waitqueue_active(&frame->wq)) wake_up_interruptible(&frame->wq); } // If someone decided to wait for ANY frame - wake him up if (waitqueue_active(&spca50x->wq)) wake_up_interruptible(&spca50x->wq); #if !defined(__FreeBSD__) frame = spca50x_next_frame(spca50x, cdata); #endif } else frame->scanstate = STATE_LINES; } /* Are we in a frame? */ if (frame == NULL || frame->scanstate != STATE_LINES) continue; if (sequenceNumber != frame->last_packet + 1 && frame->last_packet != -1) { /* Note, may get one of these for the first packet after opening */ PDEBUG(2, "Out of order packet, last = %d, this = %d", frame->last_packet, sequenceNumber); } frame->last_packet = sequenceNumber; /* This is the real conversion of the raw camera data to BGR for V4L */ switch (PWC_SC(spca50x)->pwc_info.bridge) { case BRIDGE_TV8532: case BRIDGE_ETOMS: datalength -= iPix; // correct length for packet header break; } pData = cdata + iPix; // Skip packet header (1 or 10 bytes) // Consume data if (0) PDEBUG(5, "Processing packet seq %d,length %d,totlength %d", frame->last_packet, datalength, frame->totlength); /* this copy consume input data from the isoc stream */ if ((datalength > 0) && (datalength <= 0x3ff)) { memcpy(frame->highwater, pData, datalength); frame->highwater += datalength; totlen += datalength; } frame->totlength = totlen; #ifdef SPCA50X_ENABLE_EXP_BRIGHTNESS /* autoadjust is set to 1 to so now autobrightness will be calculated frome this frame. autoadjust will be set to 0 when autobrightness has been corrected if needed. */ autoadjust = 1; #endif /* SPCA50X_ENABLE_EXP_BRIGHTNESS */ #if defined(__FreeBSD__) frame->filled = frame->highwater - frame->data; #endif } return totlen; } /** * Reset the camera and send the correct initialization sequence for the * currently selected source */ int spca50x_init_source(struct usb_spca50x *spca50x) { int err_code; struct usb_device *dev = spca50x->dev; int index; // index in the modes table switch (PWC_SC(spca50x)->pwc_info.bridge) { default: err("spca50x_init_source: Unimplemented bridge type %d\n", PWC_SC(spca50x)->pwc_info.bridge); return -EINVAL; case BRIDGE_EM2820: err_code = em2820_init(spca50x); break; case BRIDGE_SN9CXXX: err_code = sn9cxxx_init(spca50x); PDEBUG(2, "Initializing Sonix finished %d", err_code); if (err_code < 0) return err_code; break; case BRIDGE_ETOMS: err_code = Et_init(spca50x); break; case BRIDGE_CX11646: err_code = cx11646_init(spca50x); break; case BRIDGE_ZC3XX: err_code = zc3xx_init(spca50x); break; case BRIDGE_SONIX: err_code = sonix_init(spca50x); PDEBUG(2, "Initializing Sonix finished"); break; case BRIDGE_SPCA500: /* initialisation of spca500 based cameras is deferred */ PDEBUG(2, "Initializing SPCA500 started"); if (spca50x->desc == LogitechClickSmart310) { spca500_clksmart310_init(spca50x); } else { spca500_initialise(spca50x); } PDEBUG(2, "Initializing SPCA500 finished"); break; case BRIDGE_SPCA501: PDEBUG(2, "Initializing SPCA501 started"); #if defined(__FreeBSD__) /* XXX */ if (spca50x->vendor == 0x0506 && spca50x->product == 0x00df) { #else if (spca50x->dev->descriptor.idVendor == 0x0506 && spca50x->dev->descriptor.idProduct == 0x00df) { #endif /* Special handling for 3com data */ spca50x_write_vector(spca50x, spca501_3com_open_data); } else if (spca50x->desc == Arowana300KCMOSCamera || spca50x->desc == SmileIntlCamera) { /* Arowana 300k CMOS Camera data */ spca50x_write_vector(spca50x, spca501c_arowana_open_data); } else if (spca50x->desc == MystFromOriUnknownCamera) { /* UnKnow CMOS Camera data */ spca50x_write_vector(spca50x, spca501c_mysterious_init_data); } else { /* Generic 501 open data */ spca50x_write_vector(spca50x, spca501_open_data); } #ifdef SPCA50X_ENABLE_EXPERIMENTAL spca50x->a_blue = spca50x_reg_read(spca50x->dev, SPCA501_REG_CCDSP, SPCA501_A11, 2) & 0xFF; spca50x->a_green = spca50x_reg_read(spca50x->dev, SPCA501_REG_CCDSP, SPCA501_A21, 2) & 0xFF; spca50x->a_red = spca50x_reg_read(spca50x->dev, SPCA501_REG_CCDSP, SPCA501_A31, 2) & 0xFF; #endif /* SPCA50X_ENABLE_EXPERIMENTAL */ PDEBUG(2, "Initializing SPCA501 finished"); break; case BRIDGE_SPCA504: case BRIDGE_SPCA504C: case BRIDGE_SPCA536: case BRIDGE_SPCA533: case BRIDGE_SPCA504B: PDEBUG(2, "Opening SPCA5xx FW2"); sp5xxfw2_init(spca50x); break; case BRIDGE_SPCA505: PDEBUG(2, "Initializing SPCA505"); if (spca50x->desc == Nxultra) { spca50x_write_vector(spca50x, spca505b_open_data_ccd); } else { spca50x_write_vector(spca50x, spca505_open_data_ccd); } err_code = 0; err_code = spca50x_reg_read(dev, 6, (__u16) 0x16, 2); if (err_code < 0) { PDEBUG(1, "register read failed for after vector read err = %d", err_code); return -EIO; } else { PDEBUG(3, "After vector read returns : 0x%x should be 0x0101", err_code & 0xFFFF); } err_code = spca50x_reg_write(dev, 6, 0x16, 0xa); if (err_code < 0) { PDEBUG(1, "register write failed for (6,0xa,0x16) err=%d", err_code); return -EIO; } spca50x_reg_write(spca50x->dev, 5, 0xc2, 18); break; case BRIDGE_SPCA506: spca506_init(spca50x); break; case BRIDGE_SPCA561: err_code = spca561_init(spca50x); break; case BRIDGE_SPCA508: spca50x_write_vector(spca50x, spca508_open_data); break; case BRIDGE_TV8532: /* To be sure that the init array is received by the cam, we send it 3 times. Yes, i'm tired of crashing, when trying to get a stream from a uninitalized cam... We probably should make some fancy i2c stuff, but hey, if it ain't broken don't fix it ;) BTW. it's broken! */ err_code = tv8532_init(spca50x); break; case BRIDGE_PAC207: err_code = pac207_init(spca50x); break; case BRIDGE_MR97311: break; case BRIDGE_STV0602: err_code = qc_sensor_init(spca50x); break; } spca50x->norme = 0; spca50x->channel = 0; switch (PWC_SC(spca50x)->pwc_info.bridge) { case BRIDGE_ZC3XX: case BRIDGE_SONIX: case BRIDGE_SN9CXXX: case BRIDGE_ETOMS: case BRIDGE_SPCA561: case BRIDGE_SPCA504: case BRIDGE_SPCA504B: case BRIDGE_SPCA504C: case BRIDGE_SPCA533: case BRIDGE_SPCA536: case BRIDGE_STV0602: case BRIDGE_EM2820: case BRIDGE_PAC207: PDEBUG(2, "Set mode asked w %d h %d p %d", spca50x->width, spca50x->height, spca50x->format); err_code = spca5xx_setMode(spca50x, spca50x->width, spca50x->height, spca50x->format); PDEBUG(2, "set Mode return %d ", err_code); if (err_code < 0) return -EINVAL; break; default: if ((index = spca50x_find_mode_index(spca50x->width, spca50x->height, GET_EXT_MODES(PWC_SC(spca50x)->pwc_info.bridge))) == -EINVAL) { err("Can't find proper mode?!!"); } else { err_code = spca50x_set_mode(spca50x, (index & 0x0F), GET_EXT_MODES(PWC_SC(spca50x)->pwc_info.bridge)); if (err_code) { err("Can't set proper camera mode"); return -EINVAL; } } break; } return 0; } /**************************************************************************** * * V4L API * ***************************************************************************/ static inline void spca5xx_setFrameDecoder(struct usb_spca50x *spca50x) { int i; /* configure the frame detector with default parameters */ memset(&spca50x->pictsetting, 0, sizeof(struct pictparam)); spca50x->pictsetting.change = 0x01; spca50x->pictsetting.force_rgb = force_rgb; spca50x->pictsetting.gamma = gamma; spca50x->pictsetting.OffRed = OffRed; spca50x->pictsetting.OffBlue = OffBlue; spca50x->pictsetting.OffGreen = OffGreen; spca50x->pictsetting.GRed = GRed; spca50x->pictsetting.GBlue = GBlue; spca50x->pictsetting.GGreen = GGreen; i = 0; /* silence compiler */ #if !defined(__FreeBSD__) /* Set default sizes in case IOCTL (VIDIOCMCAPTURE) is not used * (using read() instead). */ for (i = 0; i < SPCA50X_NUMFRAMES; i++) { spca50x->frame[i].width = spca50x->width; spca50x->frame[i].height = spca50x->height; spca50x->frame[i].cameratype = spca50x->cameratype; spca50x->frame[i].scanlength = spca50x->width * spca50x->height * 3 / 2; // assumes 4:2:0 data spca50x->frame[i].depth = 24; /* Note: format reflects format of data as returned to * a process, not as read from camera hardware. * This might be a good idea for dumb programs always * assuming the following settings. */ spca50x->frame[i].format = VIDEO_PALETTE_RGB24; } #endif /* !__FreeBSD__ */ // spca50x->format = VIDEO_PALETTE_RGB24; spca50x->format = VIDEO_PALETTE_YUV420P; /* XXX default format */ } static inline void spca5xx_initDecoder(struct usb_spca50x *spca50x) { if (spca50x->cameratype == JPGH || spca50x->cameratype == JPGC || spca50x->cameratype == JPGS || spca50x->cameratype == JPEG || spca50x->cameratype == JPGM) init_jpeg_decoder(spca50x); if (PWC_SC(spca50x)->pwc_info.bridge == BRIDGE_SONIX) init_sonix_decoder(spca50x); if (PWC_SC(spca50x)->pwc_info.bridge == BRIDGE_PAC207) init_pixart_decoder(spca50x); } static inline void spca5xx_chgAuto(struct usb_spca50x *spca50x, __u8 autoval) { spca50x->autoexpo = (int) autoval; switch (PWC_SC(spca50x)->pwc_info.bridge) { case BRIDGE_ZC3XX: zc3xx_setAutobright(spca50x); break; default: break; } } static inline void spca5xx_chgQtable(struct usb_spca50x *spca50x, __u8 qtable) { /* one frame maybe corrupted wait for the result */ switch (PWC_SC(spca50x)->pwc_info.bridge) { case BRIDGE_ZC3XX: spca50x->qindex = (int) qtable; zc3xx_setquality(spca50x); init_jpeg_decoder(spca50x); break; default: break; } } static inline void spca5xx_chgDtimes(struct usb_spca50x *spca50x, __u16 dtimes) { unsigned long flags; spin_lock_irqsave(&spca50x->v4l_lock, flags); spca50x->dtimes = (unsigned int) dtimes; spin_unlock_irqrestore(&spca50x->v4l_lock, flags); } #if !defined(__FreeBSD__) static int spca5xx_open(struct inode *inode, struct file *file) { ... /* initialize sensor and decoding */ err = spca50x_init_source(spca50x); spca5xx_initDecoder(spca50x); spca5xx_setFrameDecoder(spca50x); err = spca50x_init_isoc(spca50x); /* Now, let's get brightness from the camera */ spca50x->brightness = spca50x_get_brightness(spca50x) << 8; spca50x->whiteness = 0; return err; } #endif /* !__FreeBSD__ */ static void inline spcaCameraShutDown(struct usb_spca50x *spca50x) { if (spca50x->dev) { switch (PWC_SC(spca50x)->pwc_info.bridge) { case BRIDGE_SPCA505: spca50x_reg_write(spca50x->dev, 0x3, 0x3, 0x20); // This maybe reset or power control spca50x_reg_write(spca50x->dev, 0x3, 0x1, 0x0); spca50x_reg_write(spca50x->dev, 0x3, 0x0, 0x1); spca50x_reg_write(spca50x->dev, 0x5, 0x10, 0x1); spca50x_reg_write(spca50x->dev, 0x5, 0x11, 0xF); break; case BRIDGE_SPCA501: // This maybe reset or power control spca50x_reg_write(spca50x->dev, SPCA501_REG_CTLRL, 0x5, 0x0); break; case BRIDGE_ZC3XX: zc3xx_shutdown(spca50x); break; case BRIDGE_SPCA561: spca561_shutdown(spca50x); break; case BRIDGE_PAC207: pac207_shutdown(spca50x); break; } } } #if !defined(__FreeBSD__) #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,22) static int spca5xx_close(struct inode *inode, struct file *file) { struct video_device *vdev = file->private_data; #else static void spca5xx_close(struct video_device *vdev) { #endif struct usb_spca50x *spca50x = video_get_drvdata(vdev); int i; PDEBUG(2, "spca50x_close"); down(&spca50x->lock); spca50x->user--; spca50x->curframe = -1; if (spca50x->present) { spca50x_stop_isoc(spca50x); spcaCameraShutDown(spca50x); for (i = 0; i < SPCA50X_NUMFRAMES; i++) { if (waitqueue_active(&spca50x->frame[i].wq)) wake_up_interruptible(&spca50x->frame[i].wq); } if (waitqueue_active(&spca50x->wq)) wake_up_interruptible(&spca50x->wq); } /* times to dealloc ressource */ up(&spca50x->lock); spca5xx_dealloc(spca50x); PDEBUG(2, "Release ressources done"); #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) MOD_DEC_USE_COUNT; #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,22) file->private_data = NULL; return 0; #endif } #endif /* !__FreeBSD__ */ static int spca5xx_testPalSize( struct usb_spca50x *spca50x, int pal, int w, int h) { int needpalette; int needsize; if ((needpalette = v4l_to_spca5xx(pal)) < 0) return -EINVAL; if((needsize= wxh_to_size(w,h)) < 0) return -EINVAL; if(!(spca50x->mode_cam[needsize].t_palette & needpalette)) return -EINVAL; return 0; } #if defined(__FreeBSD__) int spca5xx_ioctl_helper(struct usb_spca50x *spca50x, unsigned int cmd, void *arg) { #else #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,22) static int spca5xx_do_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *arg) { struct video_device *vdev = file->private_data; #else static int spca5xx_ioctl(struct video_device *vdev, unsigned int cmd, void *arg) { #endif struct usb_spca50x *spca50x = video_get_drvdata(vdev); PDEBUG(2, "do_IOCtl: 0x%X", cmd); if (!spca50x->dev) return -EIO; #endif /* !__FreeBSD__ */ switch (cmd) { case VIDIOCGCAP: { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,22) struct video_capability *b = arg; #else struct video_capability j; struct video_capability *b = &j; #endif PDEBUG(2, "VIDIOCGCAP %p :", b); memset(b, 0, sizeof(struct video_capability)); snprintf(b->name, 32, "%s", camera_name(spca50x->desc)); b->type = VID_TYPE_CAPTURE; b->channels = ((PWC_SC(spca50x)->pwc_info.bridge == BRIDGE_SPCA506) ? 8 : 1); b->audios = 0; b->maxwidth = spca50x->maxwidth; b->maxheight = spca50x->maxheight; b->minwidth = spca50x->minwidth; b->minheight = spca50x->minheight; #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,22) if (copy_to_user(arg, b, sizeof(struct video_capability))) return -EFAULT; #endif return 0; } case VIDIOCGCHAN: { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,22) struct video_channel *v = arg; #else struct video_channel k; struct video_channel *v = &k; if (copy_from_user(v, arg, sizeof(struct video_channel))) return -EFAULT; #endif switch (PWC_SC(spca50x)->pwc_info.bridge) { case BRIDGE_SPCA500: case BRIDGE_SPCA501: case BRIDGE_SPCA504: case BRIDGE_SPCA504B: case BRIDGE_SPCA504C: case BRIDGE_SPCA508: case BRIDGE_SPCA533: case BRIDGE_SPCA536: case BRIDGE_SPCA561: case BRIDGE_SONIX: case BRIDGE_ZC3XX: case BRIDGE_CX11646: case BRIDGE_TV8532: case BRIDGE_ETOMS: case BRIDGE_SN9CXXX: case BRIDGE_MR97311: case BRIDGE_PAC207: snprintf(v->name, 32, "%s", bridge_name(PWC_SC(spca50x)->pwc_info.bridge)); break; case BRIDGE_SPCA505: strncpy(v->name, ((v->channel == 0) ? "SPCA505" : "Video In"), 32); break; case BRIDGE_SPCA506: spca506_GetNormeInput(spca50x, (__u16 *) & (v->norm), (__u16 *) & (v->channel)); if (v->channel < 4) { snprintf(v->name, 32, "SPCA506-CBVS-%d", v->channel); } else { snprintf(v->name, 32, "SPCA506-S-Video-%d", v->channel); } break; } v->flags = 0; v->tuners = 0; v->type = VIDEO_TYPE_CAMERA; #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,22) if (copy_to_user(arg, v, sizeof(struct video_channel))) return -EFAULT; #endif return 0; } case VIDIOCSCHAN: { /* There seems to be some confusion as to whether this is a struct video_channel or an int. This should be safe as the first element of video_channel is an int channel number and we just ignore the rest */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,22) struct video_channel *v = arg; #else struct video_channel k; struct video_channel *v = &k; if (copy_from_user(v, arg, sizeof(struct video_channel))) return -EFAULT; #endif /* exclude hardware channel reserved */ if ((v->channel < 0) || (v->channel > 9) || (v->channel == 4) || (v->channel == 5)) return -EINVAL; if (PWC_SC(spca50x)->pwc_info.bridge == BRIDGE_SPCA506) { spca506_SetNormeInput(spca50x, v->norm, v->channel); } return 0; } case VIDIOCGPICT: /* XXX todo: move to the pwc.c code */ { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,22) struct video_picture *p = arg; #else struct video_picture p1; struct video_picture *p = &p1; #endif #if !defined(__FreeBSD__) p->depth = spca50x->frame[0].depth; #endif p->palette = spca50x->format; #ifdef SPCA50X_ENABLE_EXPERIMENTAL if (!autoadjust) p->brightness = spca50x_get_brightness(spca50x) << 8; #else /* SPCA50X_ENABLE_EXPERIMENTAL */ p->brightness = spca50x_get_brightness(spca50x) << 8; #endif /* SPCA50X_ENABLE_EXPERIMENTAL */ PDEBUG(4, "VIDIOCGPICT: depth=%d, palette=%d", p->depth, p->palette); switch (PWC_SC(spca50x)->pwc_info.bridge) { case BRIDGE_SPCA506: spca506_GetBrightContrastHueColors(spca50x, &p->brightness, &p->contrast, &p->hue, &p->colour); p->whiteness = 0; break; case BRIDGE_PAC207: spca50x->brightness = pac207_getbrightness(spca50x); spca50x->contrast = pac207_getcontrast(spca50x); p->brightness = spca50x->brightness; p->contrast = spca50x->contrast; p->colour = 0; break; case BRIDGE_SN9CXXX: //spca50x->brightness = sn9cxxx_getbrightness(spca50x); sn9cxxx_getcontrast(spca50x); //spca50x->colour = sn9cxxx_getcolors(spca50x); p->brightness = spca50x->brightness; p->contrast = spca50x->contrast; p->colour = spca50x->colour; break; case BRIDGE_ETOMS: spca50x->brightness = Et_getbrightness(spca50x); spca50x->contrast = Et_getcontrast(spca50x); spca50x->colour = Et_getcolors(spca50x); p->brightness = spca50x->brightness; p->contrast = spca50x->contrast; p->colour = spca50x->colour; break; case BRIDGE_SPCA561: spca561_getbrightness(spca50x); spca561_getcontrast(spca50x); p->brightness = spca50x->brightness; p->contrast = spca50x->contrast; break; case BRIDGE_SPCA500: { int tmp; p->colour = 0; //0x8169 p->whiteness = 0; p->contrast = spca50x_reg_read(spca50x->dev, 0x0, 0x8168, 1) << 8; //0x8162 p->brightness = spca50x_get_brightness(spca50x); /* get hue */ tmp = (spca50x_reg_read(spca50x->dev, 0x0, 0x816b, 1) & 12) << 6; tmp += spca50x_reg_read(spca50x->dev, 0x0, 0x816a, 1); p->hue = tmp << 6; PDEBUG(2, "brightness = %d", p->brightness); break; } case BRIDGE_SPCA501: #ifdef SPCA50X_ENABLE_EXPERIMENTAL p->whiteness = spca50x_get_whiteness(spca50x) << 8; if (autoadjust) p->brightness = p->whiteness; p->contrast = ((spca50x_reg_read(spca50x->dev, SPCA501_REG_CCDSP, SPCA501_A11, 2) & 0xFF) - spca50x->a_blue) << 8; printk("Contrast = %d\n", p->contrast >> 8); #else /* SPCA50X_ENABLE_EXPERIMENTAL */ p->whiteness = 0; p->contrast = 0; #endif /* SPCA50X_ENABLE_EXPERIMENTAL */ p->colour = (spca50x_reg_read(spca50x->dev, SPCA501_REG_CCDSP, 0x11, 2) & 0xFF) << 8; p->hue = (spca50x_reg_read (spca50x->dev, SPCA501_REG_CCDSP, 0x13, 2) & 0xFF) << 8; break; case BRIDGE_SPCA536: case BRIDGE_SPCA533: case BRIDGE_SPCA504: case BRIDGE_SPCA504B: case BRIDGE_SPCA504C: p->colour = sp5xxfw2_getcolors(spca50x); p->whiteness = 0; p->contrast = sp5xxfw2_getcontrast(spca50x); p->brightness = spca50x_get_brightness(spca50x); p->hue = 0; PDEBUG(4, "color: 0x%02x contrast: 0x%02x brightness: 0x%02x hue: 0x%02x", p->colour, p->contrast, p->brightness, p->hue); break; #ifdef SPCA50X_ENABLE_EXP_BRIGHTNESS case BRIDGE_SPCA505: p->whiteness = (spca50x_reg_read(spca50x->dev, 6, 0x59, 2) * 10) << 8; p->contrast = spca50x_reg_read(spca50x->dev, 5, 0xc2, 2) << 8; p->colour = spca50x_reg_read(spca50x->dev, 5, 0xc2, 2) << 8; p->hue = spca50x_reg_read(spca50x->dev, 5, 0xc1, 2) << 8; break; #endif /* SPCA50X_ENABLE_EXP_BRIGHTNESS */ default: /* default to not allowing any settings to change */ p->colour = spca50x->colour; p->whiteness = spca50x->whiteness; p->contrast = spca50x->contrast; p->brightness = spca50x->brightness; p->hue = spca50x->hue; break; } #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,22) if (copy_to_user(arg, p, sizeof(struct video_picture))) return -EFAULT; #endif return 0; } case VIDIOCSPICT: { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,22) struct video_picture *p = arg; #else struct video_picture p1; struct video_picture *p = &p1; if (copy_from_user(p, arg, sizeof(struct video_picture))) return -EFAULT; #endif PDEBUG(4, "VIDIOCSPICT"); if(spca5xx_testPalSize(spca50x,p->palette,spca50x->width,spca50x->height) < 0) return -EINVAL; if (spca50x->format != p->palette) { /* FIXME test if the palette is available */ PDEBUG(4, "Setting depth=%d, palette=%d", p->depth, p->palette); #if !defined(__FreeBSD__) int i; /* change the output palette the input stream is the same */ /* no need to stop the camera streaming and restart */ for (i = 0; i < SPCA50X_NUMFRAMES; i++) { spca50x->frame[i].depth = p->depth; spca50x->frame[i].format = p->palette; } #endif /* !__FreeBSD__ */ spca50x->format = p->palette; } switch (PWC_SC(spca50x)->pwc_info.bridge) { case BRIDGE_SPCA506: spca506_SetBrightContrastHueColors(spca50x, p->brightness, p->contrast, p->hue, p->colour); break; case BRIDGE_PAC207: spca50x->contrast = p->contrast; spca50x->brightness = p->brightness; pac207_setbrightness(spca50x); pac207_setcontrast(spca50x); break; case BRIDGE_SN9CXXX: spca50x->contrast = p->contrast; spca50x->brightness = p->brightness; sn9cxxx_setbrightness(spca50x); sn9cxxx_setcontrast(spca50x); break; case BRIDGE_ETOMS: spca50x->contrast = p->contrast; spca50x->brightness = p->brightness; spca50x->colour = p->colour; Et_setbrightness(spca50x); Et_setcontrast(spca50x); Et_setcolors(spca50x); break; case BRIDGE_ZC3XX: spca50x->contrast = p->contrast; spca50x->brightness = p->brightness; zc3xx_setbrightness(spca50x); zc3xx_setcontrast(spca50x); break; case BRIDGE_SONIX: spca50x_set_brightness(spca50x, p->brightness >> 8); spca50x->contrast = p->contrast; sonix_setcontrast(spca50x); break; case BRIDGE_TV8532: spca50x_set_brightness(spca50x, p->brightness >> 8); spca50x->contrast = p->contrast; tv8532_setcontrast(spca50x); break; case BRIDGE_CX11646: spca50x_set_brightness(spca50x, p->brightness >> 8); spca50x->contrast = p->contrast; cx_setcontrast(spca50x); spca50x->colour = p->colour; break; case BRIDGE_SPCA561: spca50x->brightness = p->brightness; spca50x->contrast = p->contrast; spca561_setbrightness(spca50x); spca561_setcontrast(spca50x); break; case BRIDGE_SPCA500: { int tmp; spca50x_set_brightness(spca50x, p->brightness >> 8); //((p.brightness >> 8) + 128) % 255); spca50x_reg_write(spca50x->dev, 0x00, 0x8168, p->contrast >> 8); /* set hue */ tmp = spca50x_reg_read(spca50x->dev, 0x00, 0x816b, 1); tmp = tmp & 0xf3; tmp += (p->hue & 0xc000) >> 12; spca50x_reg_write(spca50x->dev, 0x00, 0x816b, tmp); spca50x_reg_write(spca50x->dev, 0x00, 0x816a, (p->hue & 0x3fff) >> 8); } break; case BRIDGE_SPCA501: { #ifdef SPCA50X_ENABLE_EXPERIMENTAL __u16 kred, kgreen, kblue; //coeffs of color correction if (autoadjust) p->whiteness = p->brightness; spca50x_set_whiteness(spca50x, p->whiteness >> 8); /* Setting "contrast" (offset to color correction parameters) */ p->contrast >>= 8; kred = spca50x->a_red + p->contrast; kgreen = spca50x->a_green + p->contrast; kblue = spca50x->a_blue + p->contrast; PDEBUG(4, "Setting red = %d, green = %d, blue = %d", (__u8) (kred & 0xFF), (__u8) (kgreen & 0xFF), (__u8) (kblue & 0xFF)); if (kred < 256 && kgreen < 256 && kblue < 256) { spca50x_reg_write(spca50x->dev, SPCA501_REG_CCDSP, SPCA501_A11, (kblue & 0xFF)); spca50x_reg_write(spca50x->dev, SPCA501_REG_CCDSP, SPCA501_A21, (kgreen & 0xFF)); spca50x_reg_write(spca50x->dev, SPCA501_REG_CCDSP, SPCA501_A31, (kred & 0xFF)); } spca50x_reg_write(spca50x->dev, SPCA501_REG_CCDSP, 0x11, ((p->colour >> 8) & 0xFF)); spca50x_reg_write(spca50x->dev, SPCA501_REG_CCDSP, 0x13, ((p->hue >> 8) & 0xFF)); #endif /* SPCA50X_ENABLE_EXPERIMENTAL */ /* Setting brightness */ PDEBUG(4, "Setting brightness = %d", p->brightness); //// This call must be moved to better place #ifdef SPCA50X_ENABLE_EXPERIMENTAL if (!autoadjust) spca50x_set_brightness(spca50x, p->brightness >> 8); #else /* SPCA50X_ENABLE_EXPERIMENTAL */ spca50x_set_brightness(spca50x, p->brightness >> 8); #endif /* SPCA50X_ENABLE_EXPERIMENTAL */ break; } case BRIDGE_SPCA536: case BRIDGE_SPCA533: case BRIDGE_SPCA504: case BRIDGE_SPCA504B: case BRIDGE_SPCA504C: { spca50x_set_brightness(spca50x, ((p->brightness >> 8) + 128) % 255); spca50x->contrast = p->contrast; spca50x->colour = p->colour; sp5xxfw2_setcolors(spca50x); sp5xxfw2_setcontrast(spca50x); p->whiteness = 0; break; } case BRIDGE_SPCA505: { spca50x_set_brightness(spca50x, p->brightness >> 8); #if 0 //#ifdef SPCA50X_ENABLE_EXP_BRIGHTNESS spca50x_reg_write(spca50x->dev, 6, 0x1f, 0x16); spca50x_reg_write(spca50x->dev, 6, 0x20, 0x0); spca50x_reg_write(spca50x->dev, 5, 0xc1, p->hue >> 8); spca50x_reg_write(spca50x->dev, 5, 0xc2, p->contrast >> 8); spca50x_reg_write(spca50x->dev, 5, 0xca, 0x0); ////I have no this type of camera. Please, check this and move this // call to better place spca50x_set_brightness(spca50x, p->brightness >> 8); //// spca50x_reg_write(spca50x->dev, 6, 0x59, max((p->whiteness >> 8) / 10, 20)); #endif /* SPCA50X_ENABLE_EXP_BRIGHTNESS */ break; } default: { /* default to not allowing any settings to change ?? */ p->colour = spca50x->colour; p->whiteness = spca50x->whiteness; p->contrast = spca50x->contrast; p->brightness = spca50x->brightness; p->hue = spca50x->hue; break; } } spca50x->contrast = p->contrast; spca50x->brightness = p->brightness; spca50x->colour = p->colour; spca50x->hue = p->hue; spca50x->whiteness = p->whiteness; #ifdef SPCA50X_ENABLE_EXPERIMENTAL spca50x->nstable = 0; spca50x->nunstable = 0; #endif /* SPCA50X_ENABLE_EXPERIMENTAL */ return 0; } case VIDIOCGCAPTURE: { int *vf = arg; PDEBUG(4, "VIDIOCGCAPTURE"); *vf = 0; // no subcapture return -EINVAL; } case VIDIOCSCAPTURE: { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,22) struct video_capture *vc = arg; #else struct video_capture vc1; struct video_capture *vc = &vc1; if (copy_from_user(vc, arg, sizeof(struct video_capture))) return -EFAULT; #endif if (vc->flags) return -EINVAL; if (vc->decimation) return -EINVAL; return -EINVAL; } case VIDIOCSWIN: { int result; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,22) struct video_window *vw = arg; #else struct video_window vw1; struct video_window *vw = &vw1; if (copy_from_user(vw, arg, sizeof(struct video_window))) return -EFAULT; #endif PDEBUG(3, "VIDIOCSWIN: width=%d, height=%d, flags=0x%x x %d y %d", vw->width, vw->height, vw->flags, vw->x, vw->y); if(spca5xx_testPalSize(spca50x,spca50x->format,vw->width,vw->height) < 0) return -EINVAL; if (vw->x) return -EINVAL; if (vw->y) return -EINVAL; if (vw->width > (unsigned int) spca50x->maxwidth) return -EINVAL; if (vw->height > (unsigned int) spca50x->maxheight) return -EINVAL; if (vw->width < (unsigned int) spca50x->minwidth) return -EINVAL; if (vw->height < (unsigned int) spca50x->minheight) return -EINVAL; switch (PWC_SC(spca50x)->pwc_info.bridge) { case BRIDGE_ZC3XX: case BRIDGE_SONIX: case BRIDGE_SN9CXXX: case BRIDGE_ETOMS: case BRIDGE_SPCA561: case BRIDGE_SPCA504: case BRIDGE_SPCA504B: case BRIDGE_SPCA504C: case BRIDGE_SPCA533: case BRIDGE_SPCA536: case BRIDGE_PAC207: result = spca5xx_restartMode(spca50x, vw->width, vw->height, PWC_SC(spca50x)->vpalette); break; default: result = spca50x_mode_init_regs(spca50x, vw->width, vw->height, PWC_SC(spca50x)->vpalette, GET_EXT_MODES(PWC_SC(spca50x)->pwc_info.bridge)); } #if !defined(__FreeBSD__) if (result == 0) { spca50x->frame[0].width = vw->width; spca50x->frame[0].height = vw->height; } #endif /* !__FreeBSD__ */ return result; } case VIDIOCGWIN: { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,22) struct video_window *vw = arg; #else struct video_window vw1; struct video_window *vw = &vw1; #endif memset(vw, 0, sizeof(struct video_window)); vw->x = 0; vw->y = 0; #if !defined(__FreeBSD__) vw->width = spca50x->frame[0].width; vw->height = spca50x->frame[0].height; #endif /* !__FreeBSD__ */ vw->flags = 0; PDEBUG(4, "VIDIOCGWIN: %dx%d", vw->width, vw->height); #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,22) if (copy_to_user(arg, vw, sizeof(struct video_capture))) return -EFAULT; #endif return 0; } case VIDIOCGMBUF: { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,22) struct video_mbuf *vm = arg; #else struct video_mbuf vm1; struct video_mbuf *vm = &vm1; #endif int i; PDEBUG(2, "VIDIOCGMBUF: %p ", vm); memset(vm, 0, sizeof(struct video_mbuf)); vm->size = SPCA50X_NUMFRAMES * MAX_DATA_SIZE; vm->frames = SPCA50X_NUMFRAMES; for (i = 0; i < SPCA50X_NUMFRAMES; i++) { vm->offsets[i] = MAX_DATA_SIZE * i; } #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,22) if (copy_to_user(arg, vm, sizeof(struct video_mbuf))) return -EFAULT; #endif return 0; } case VIDIOCMCAPTURE: { int depth; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,22) struct video_mmap *vm = arg; #else struct video_mmap vm1; struct video_mmap *vm = &vm1; if (copy_from_user(vm, arg, sizeof(struct video_mmap))) return -EFAULT; #endif PDEBUG(4, "CMCAPTURE"); PDEBUG(4, "CM frame: %d, size: %dx%d, format: %d", vm->frame, vm->width, vm->height, vm->format); depth = spca5xx_get_depth(spca50x, vm->format); if (!depth || depth < spca50x->min_bpp) { err("VIDIOCMCAPTURE: invalid format (%d)", vm->format); return -EINVAL; } if ((vm->frame < 0) || (vm->frame > 3)) { err("VIDIOCMCAPTURE: invalid frame (%d)", vm->frame); return -EINVAL; } if (vm->width > spca50x->maxwidth || vm->height > spca50x->maxheight) { err("VIDIOCMCAPTURE: requested dimensions too big"); return -EINVAL; } if (vm->width < spca50x->minwidth || vm->height < spca50x->minheight) { err("VIDIOCMCAPTURE: requested dimensions too small"); return -EINVAL; } if(spca5xx_testPalSize(spca50x,vm->format,vm->width,vm->height) < 0) return -EINVAL; /* * If we are grabbing the current frame, let it pass */ /* why should i wait here CSYNCHRO do the job if (spca50x->frame[vm->frame].grabstate == FRAME_GRABBING) { int ret; PDEBUG (4, "MCAPTURE: already grabbing"); // ret = wait_event_interruptible (spca50x->wq, ret = wait_event_interruptible (spca50x->frame[vm->frame].wq, (spca50x->frame[vm->frame]. grabstate != FRAME_GRABBING)); if (ret) return -EINTR; } */ #if !defined(__FreeBSD__) /* XXX watch out this one */ if ((spca50x->frame[vm->frame].width != vm->width) || (spca50x->frame[vm->frame].height != vm->height) || (spca50x->frame[vm->frame].format != vm->format)) { int ret; if (spca50x->bridge == BRIDGE_ZC3XX || spca50x->bridge == BRIDGE_SONIX || spca50x->bridge == BRIDGE_SN9CXXX || spca50x->bridge == BRIDGE_ETOMS || spca50x->bridge == BRIDGE_SPCA561 || spca50x->bridge == BRIDGE_SPCA504 || spca50x->bridge == BRIDGE_SPCA504B || spca50x->bridge == BRIDGE_SPCA504C || spca50x->bridge == BRIDGE_SPCA533 || spca50x->bridge == BRIDGE_SPCA536 || spca50x->bridge == BRIDGE_PAC207) { ret = spca5xx_restartMode(spca50x, vm->width, vm->height, vm->format); } else { ret = spca50x_mode_init_regs(spca50x, vm->width, vm->height, vm->format, GET_EXT_MODES(spca50x-> bridge)); } if (ret < 0) return ret; spca50x->frame[vm->frame].width = vm->width; spca50x->frame[vm->frame].height = vm->height; spca50x->frame[vm->frame].format = vm->format; spca50x->frame[vm->frame].depth = depth; } #endif /* !__FreeBSD__ */ if (spca50x->autoexpo) { /* set the autoexpo here */ switch (PWC_SC(spca50x)->pwc_info.bridge) { case BRIDGE_SPCA561: spca561_setAutobright(spca50x); break; case BRIDGE_ETOMS: PDEBUG(5, "Etoms AutoBrightness"); Et_setAutobright(spca50x); break; case BRIDGE_PAC207: PDEBUG(5, "Pac207 AutoBrightness"); pac207_setAutobright(spca50x); break; #if 0 case BRIDGE_SN9CXXX: sn9c102p_setAutobright(spca50x); break; #endif } } #ifdef SPCA5XX_ENABLE_REGISTERPLAY __u8 Rval = 0; if (RegStrobe != 0) { if (RegStrobe == 1) { if (PWC_SC(spca50x)->pwc_info.bridge == BRIDGE_PAC207) { pac207_RegWrite(spca50x); } else { Rval = RegValue & 0xFF; spca5xxRegWrite(spca50x->dev, 0xa0, Rval, (__u16) (RegAddress & 0xFFFF), NULL, 0); } } else { if (PWC_SC(spca50x)->pwc_info.bridge == BRIDGE_PAC207) { pac207_RegRead(spca50x); } else { usb_rd_vend_dev(spca50x->dev, 0xa1, 0x01, (__u16) (RegAddress & 0xFFFF), &Rval, 1); // read Lowbyte RegValue = Rval; } } RegStrobe = 0; } #endif /* SPCA5XX_ENABLE_REGISTERPLAY */ #if !defined(__FreeBSD__) /* Mark it as ready */ spca50x->frame[vm->frame].grabstate = FRAME_READY; #endif /* !__FreeBSD__ */ return 0; } case VIDIOCSYNC: { #if !defined(__FreeBSD__) int ret; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,22) unsigned int frame = *((unsigned int *) arg); #else unsigned int frame; if (copy_from_user((void *) &frame, arg, sizeof(int))) return -EFAULT; #endif PDEBUG(4, "syncing to frame %d, grabstate = %d", frame, spca50x->frame[frame].grabstate); switch (spca50x->frame[frame].grabstate) { case FRAME_UNUSED: return -EINVAL; case FRAME_ABORTING: return -ENODEV; case FRAME_READY: case FRAME_GRABBING: case FRAME_ERROR: redo: if (!spca50x->dev) return -EIO; ret = wait_event_interruptible(spca50x->frame[frame].wq, (spca50x->frame[frame]. grabstate == FRAME_DONE)); if (ret) return -EINTR; PDEBUG(4, "Synch Ready on frame %d, grabstate = %d", frame, spca50x->frame[frame].grabstate); if (spca50x->frame[frame].grabstate == FRAME_ERROR) { goto redo; } /* Fallthrough. * We have waited in state FRAME_GRABBING until it * becomes FRAME_DONE, so now we can move along. */ case FRAME_DONE: /* Release the current frame. This means that it * will be reused as soon as all other frames are * full, so the app better be done with it quickly. * Can this be avoided somehow? */ spca50x->frame[frame].grabstate = FRAME_UNUSED; PDEBUG(4, "Release frame %d state %d\n", frame, spca50x->frame[frame].grabstate); break; } /* end switch */ #endif /* !__FreeBSD__ */ return 0; } case VIDIOCGFBUF: { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,22) struct video_buffer *vb = arg; #else struct video_buffer vb1; struct video_buffer *vb = &vb1; #endif memset(vb, 0, sizeof(struct video_buffer)); vb->base = NULL; /* frame buffer not supported, not used */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,22) if (copy_to_user(arg, vb, sizeof(struct video_buffer))) return -EFAULT; #endif return 0; } case SPCAGVIDIOPARAM: { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,22) struct video_param *vp = arg; #else struct video_param vp1; struct video_param *vp = &vp1; #endif vp->autobright = (__u8) spca50x->autoexpo; vp->quality = (__u8) spca50x->qindex; vp->time_interval = (__u16) spca50x->dtimes; #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,22) if (copy_to_user(arg, vp, sizeof(struct video_param))) return -EFAULT; #endif return 0; } case SPCASVIDIOPARAM: { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,22) struct video_param *vp = arg; #else struct video_param vp1; struct video_param *vp = &vp1; if (copy_from_user(vp, arg, sizeof(struct video_param))) return -EFAULT; #endif switch (vp->chg_para) { case CHGABRIGHT: spca5xx_chgAuto(spca50x, vp->autobright); break; case CHGQUALITY: spca5xx_chgQtable(spca50x, vp->quality); break; case CHGTINTER: spca5xx_chgDtimes(spca50x, vp->time_interval); break; default: return -EINVAL; break; } return 0; } /************************************/ case VIDIOCKEY: return 0; case VIDIOCCAPTURE: return -EINVAL; case VIDIOCSFBUF: return -EINVAL; case VIDIOCGTUNER: case VIDIOCSTUNER: return -EINVAL; case VIDIOCGFREQ: case VIDIOCSFREQ: return -EINVAL; case VIDIOCGAUDIO: case VIDIOCSAUDIO: return -EINVAL; default: return -ENOIOCTLCMD; } /* end switch */ return 0; } #if !defined(__FreeBSD__) static ssize_t spca5xx_read(struct file *file, char *buf, size_t cnt, loff_t * ppos) { ... wait for a frame t be ready ... trim and copy to userspace /* set the autoexpo here */ if (spca50x->autoexpo) { /* set the autoexpo here */ if (spca50x->bridge == BRIDGE_SPCA561) spca561_setAutobright(spca50x); if (spca50x->bridge == BRIDGE_ETOMS) { PDEBUG(5, "Etoms AutoBrightness"); Et_setAutobright(spca50x); } if (spca50x->bridge == BRIDGE_PAC207) { PDEBUG(5, "Pac207 AutoBrightness"); pac207_setAutobright(spca50x); } } return count; } #endif /* !__FreeBSD__ */ /**************************************************************************** * * SPCA50X configuration * ***************************************************************************/ #define _SENS_MINMAX(wmin, hmin, wmax, hmax) do { \ spca50x->maxwidth = wmax; \ spca50x->maxheight = hmax; \ spca50x->minwidth = wmin; \ spca50x->minheight = hmin; \ } while (0) static int spca50x_configure_sensor(struct usb_spca50x *spca50x) { int err; PDEBUG(4, "starting configuration"); /* Set sensor-specific vars */ switch (PWC_SC(spca50x)->pwc_info.bridge) { case BRIDGE_SPCA500: if (spca50x->desc == LogitechClickSmart310) { _SENS_MINMAX(176, 144, 352, 288); } else { _SENS_MINMAX(176, 144, 640, 480); } break; case BRIDGE_SPCA504C: case BRIDGE_SPCA504: case BRIDGE_SPCA504B: case BRIDGE_SPCA536: case BRIDGE_SPCA533: case BRIDGE_ETOMS: case BRIDGE_ZC3XX: case BRIDGE_SONIX: case BRIDGE_SN9CXXX: case BRIDGE_SPCA561: case BRIDGE_MR97311: case BRIDGE_PAC207: case BRIDGE_STV0602: err = spca5xx_getcapability(spca50x); break; case BRIDGE_CX11646: _SENS_MINMAX(176, 144, 640, 480); break; case BRIDGE_SPCA501: case BRIDGE_SPCA506: _SENS_MINMAX(160, 120, 640, 480); break; case BRIDGE_SPCA505: if (spca50x->desc == Nxultra) { _SENS_MINMAX(160, 120, 640, 480); } else { _SENS_MINMAX(160, 120, 352, 288); } break; case BRIDGE_SPCA508: _SENS_MINMAX(160, 120, 352, 288); break; case BRIDGE_TV8532: _SENS_MINMAX(176, 144, 352, 288); break; default: _SENS_MINMAX(160, 120, 640, 480); break; } return 0; } int spca50x_configure(struct usb_spca50x *spca50x) { int i; int defaultcols = 0; int defaultrows = 0; int defaultpipe = 0; int error = 0; /* DEPRECATED spca5xx_getDefaultMode() should be use instead */ /* dynamically look up the smallest available frame size */ /* That is not really true with Hardware size need smallest and hardware */ /* Also give the pipe size for the hardware resolution */ i = spca50x_smallest_mode_index(GET_EXT_MODES(PWC_SC(spca50x)->pwc_info.bridge), &defaultcols, &defaultrows, &defaultpipe); if (i < 0) return i; PDEBUG(2, "video_register_device succeeded"); /* Initialise the camera bridge */ switch (PWC_SC(spca50x)->pwc_info.bridge) { case BRIDGE_SONIX: case BRIDGE_SN9CXXX: error = sonix_config(spca50x); break; case BRIDGE_ZC3XX: error = zc3xx_config(spca50x); break; case BRIDGE_ETOMS: error = Etxx_config(spca50x); break; case BRIDGE_SPCA561: error = spca561_config(spca50x); break; case BRIDGE_MR97311: error = mr97311_config(spca50x); break; case BRIDGE_SPCA536: case BRIDGE_SPCA533: case BRIDGE_SPCA504: case BRIDGE_SPCA504B: case BRIDGE_SPCA504C: error = sp5xxfw2_config(spca50x); break; case BRIDGE_PAC207: error = pac207_config(spca50x); break; case BRIDGE_CX11646: case BRIDGE_SPCA500: spca50x->qindex = 5; break; case BRIDGE_TV8532: tv8532_configure(spca50x); break; case BRIDGE_SPCA501: if (spca50x->desc == Arowana300KCMOSCamera || spca50x->desc == SmileIntlCamera) { /* Arowana 300k CMOS Camera data */ error = spca50x_write_vector (spca50x, spca501c_arowana_init_data); } else if (spca50x->desc == MystFromOriUnknownCamera) { /* UnKnow Ori CMOS Camera data */ error = spca50x_write_vector(spca50x, spca501c_mysterious_open_data); } else { /* generic spca501 init data */ error = spca50x_write_vector(spca50x, spca501_init_data); } break; case BRIDGE_SPCA505: /* Determine the sensor type for cameras with an internal CCD or external input */ if (spca50x->desc == Nxultra) { error = spca50x_write_vector(spca50x, spca505b_init_data); } else { error = spca50x_write_vector(spca50x, spca505_init_data); } break; case BRIDGE_SPCA506: break; case BRIDGE_SPCA508: error = config_spca508(spca50x); break; case BRIDGE_STV0602: error = config_quickcam(spca50x); break; case BRIDGE_EM2820: error = em2820_config(spca50x); break; default: err("Unknown bridge type in spca_configure\n"); error = -1; } if (error) goto error; spca50x_set_packet_size(spca50x, 0); /* Set an initial pipe size; this will be overridden by * spca50x_set_mode(), called indirectly by the open routine. */ switch (PWC_SC(spca50x)->pwc_info.bridge) { case BRIDGE_ZC3XX: case BRIDGE_SONIX: case BRIDGE_SN9CXXX: case BRIDGE_ETOMS: case BRIDGE_SPCA561: case BRIDGE_SPCA504: case BRIDGE_SPCA504B: case BRIDGE_SPCA504C: case BRIDGE_SPCA533: case BRIDGE_SPCA536: case BRIDGE_STV0602: case BRIDGE_EM2820: case BRIDGE_PAC207: i = spca5xx_getDefaultMode(spca50x); if (i < 0) return i; break; default: spca50x->pipe_size = defaultpipe; spca50x->width = defaultcols; spca50x->height = defaultrows; } spca50x->force_rgb = force_rgb; spca50x->min_bpp = min_bpp; spca50x->lum_level = lum_level; if (spca50x_configure_sensor(spca50x) < 0) { err("failed to configure"); goto error; } /* configure the frame detector with default parameters */ spca5xx_setFrameDecoder(spca50x); PDEBUG(2, "Spca5xx Configure done !!"); return 0; error: return -EBUSY; } /************************************************************************** All That stuff need to be rewrite to go in spca500.h ***************************************************************************/ static void spca500_ping310(struct usb_spca50x *spca50x) { __u8 Data[2] = { 0, 0 }; usb_rd_vend_dev(spca50x->dev, 0, 0, 0x0d04, Data, 2); PDEBUG(5, "ClickSmart310 ping 0x0d04 0x%02X 0x%02X ", Data[0], Data[1]); } static int spca500_synch310(struct usb_spca50x *spca50x) { /* Synchro the Bridge with sensor */ /* Maybe that will work on all spca500 chip */ /* because i only own a clicksmart310 try for that chip */ /* using spca50x_set_packet_size() cause an Ooops here */ /* usb_set_interface from kernel 2.6.x clear all the urb stuff */ /* up-port the same feature as in 2.4.x kernel */ __u8 Data; if (spca_set_interface(spca50x->dev, spca50x->iface, 0) < 0) { err("Set packet size: set interface error"); goto error; } spca500_ping310(spca50x); usb_rd_vend_dev(spca50x->dev, 0, 0, 0x0d00, &Data, 1); /* need alt setting here */ PDEBUG(5, "ClickSmart310 sync pipe %d altsetting %d ", spca50x->pipe_size, spca50x->alt); /* Windoze use pipe with altsetting 6 why 7 here */ if (spca_set_interface(spca50x->dev, spca50x->iface, spca50x->alt) < 0) { err("Set packet size: set interface error"); goto error; } return 0; error: return -EBUSY; } static void spca500_clksmart310_init(struct usb_spca50x *spca50x) { __u8 Data[2] = { 0, 0 }; usb_rd_vend_dev(spca50x->dev, 0, 0, 0x0d05, Data, 2); PDEBUG(5, "ClickSmart310 init 0x0d05 0x%02X 0x%02X ", Data[0], Data[1]); spca50x_reg_write(spca50x->dev, 0x00, 0x8167, 0x5a); spca500_ping310(spca50x); spca50x_reg_write(spca50x->dev, 0x00, 0x8168, 0x22); spca50x_reg_write(spca50x->dev, 0x00, 0x816a, 0xc0); spca50x_reg_write(spca50x->dev, 0x00, 0x816b, 0x0b); spca50x_reg_write(spca50x->dev, 0x00, 0x8169, 0x25); spca50x_reg_write(spca50x->dev, 0x00, 0x8157, 0x5b); spca50x_reg_write(spca50x->dev, 0x00, 0x8158, 0x5b); spca50x_reg_write(spca50x->dev, 0x00, 0x813f, 0x03); spca50x_reg_write(spca50x->dev, 0x00, 0x8151, 0x4a); spca50x_reg_write(spca50x->dev, 0x00, 0x8153, 0x78); spca50x_reg_write(spca50x->dev, 0x00, 0x0d01, 0x04); //00 for adjust shutter spca50x_reg_write(spca50x->dev, 0x00, 0x0d02, 0x01); spca50x_reg_write(spca50x->dev, 0x00, 0x8169, 0x25); spca50x_reg_write(spca50x->dev, 0x00, 0x0d01, 0x02); } void spca500_reinit(struct usb_spca50x *spca50x) { int err; __u8 Data; // some unknow command from Aiptek pocket dv and family300 spca50x_reg_write(spca50x->dev, 0x00, 0x0d01, 0x01); spca50x_reg_write(spca50x->dev, 0x00, 0x0d03, 0x00); spca50x_reg_write(spca50x->dev, 0x00, 0x0d02, 0x01); /* enable drop packet */ spca50x_reg_write(spca50x->dev, 0x00, 0x850a, 0x0001); err = spca50x_setup_qtable(spca50x, 0x00, 0x8800, 0x8840, qtable_pocketdv); if (err < 0) { PDEBUG(2, "spca50x_setup_qtable failed on init"); } /* set qtable index */ spca50x_reg_write(spca50x->dev, 0x00, 0x8880, 2); /* family cam Quicksmart stuff */ spca50x_reg_write(spca50x->dev, 0x00, 0x800a, 0x00); //Set agc transfer: synced inbetween frames spca50x_reg_write(spca50x->dev, 0x00, 0x820f, 0x01); //Init SDRAM - needed for SDRAM access spca50x_reg_write(spca50x->dev, 0x00, 0x870a, 0x04); /*Start init sequence or stream */ spca50x_reg_write(spca50x->dev, 0, 0x8003, 0x00); /* switch to video camera mode */ err = spca50x_reg_write(spca50x->dev, 0x00, 0x8000, 0x0004); wait_ms(2000); if (spca50x_reg_readwait(spca50x->dev, 0, 0x8000, 0x44) != 0) usb_rd_vend_dev(spca50x->dev, 0, 0, 0x816b, &Data, 1); spca50x_reg_write(spca50x->dev, 0x00, 0x816b, Data); } static int spca500_initialise(struct usb_spca50x *spca50x) { int index; int err; __u8 Data; int bridge = PWC_SC(spca50x)->pwc_info.bridge; /* read mode index */ index = spca50x_find_mode_index(spca50x->width, spca50x->height, GET_EXT_MODES(bridge)); if (index == -EINVAL) { PDEBUG(2, "Can't find proper mode?!!"); return -EINVAL; } /* EXPERIMENTAL Wait until test Marek */ index = index & 0x0F; spca50x_set_mode(spca50x, index, GET_EXT_MODES(bridge)); // spca50x_write_vector(spca50x, spca500_read_stats); /* is there a sensor here ? */ usb_rd_vend_dev(spca50x->dev, 0, 0, 0x8a04, &Data, 1); PDEBUG(0, "Spca500 Sensor Address 0x%02X ", Data); /* setup qtable */ switch (spca50x->desc) { case LogitechClickSmart310: //err = spca500_synch310(spca50x ); usb_rd_vend_dev(spca50x->dev, 0, 0, 0x0d00, &Data, 1); /* set x multiplier */ spca50x_reg_write(spca50x->dev, 0, 0x8001, GET_EXT_MODES(bridge)[index][3]); /* set y multiplier */ spca50x_reg_write(spca50x->dev, 0, 0x8002, GET_EXT_MODES(bridge)[index][4]); /* use compressed mode, VGA, with mode specific subsample */ spca50x_reg_write(spca50x->dev, 0, 0x8003, ((GET_EXT_MODES(bridge)[index][2] & 0x0f) << 4)); /* enable drop packet */ if ((err = spca50x_reg_write(spca50x->dev, 0x00, 0x850a, 0x0001)) < 0) { PDEBUG(2, "failed to enable drop packet"); return err; } err = spca50x_reg_write(spca50x->dev, 0x00, 0x8880, 3); if (err < 0) { PDEBUG(2, "spca50x_reg_write failed"); return err; } err = spca50x_setup_qtable(spca50x, 0x00, 0x8800, 0x8840, qtable_creative_pccam); if (err < 0) { PDEBUG(2, "spca50x_setup_qtable failed"); return err; } //Init SDRAM - needed for SDRAM access spca50x_reg_write(spca50x->dev, 0x00, 0x870a, 0x04); /* switch to video camera mode */ err = spca50x_reg_write(spca50x->dev, 0x00, 0x8000, 0x0004); if (err < 0) { PDEBUG(2, "spca50x_reg_write camera mode failed"); return err; } wait_ms(500); if (spca50x_reg_readwait(spca50x->dev, 0, 0x8000, 0x44) != 0) { PDEBUG(2, "spca50x_reg_readwait() failed"); return -EIO; } usb_rd_vend_dev(spca50x->dev, 0, 0, 0x816b, &Data, 1); spca50x_reg_write(spca50x->dev, 0x00, 0x816b, Data); err = spca500_synch310(spca50x); spca50x_write_vector(spca50x, spca500_visual_defaults); /* set x multiplier */ spca50x_reg_write(spca50x->dev, 0, 0x8001, GET_EXT_MODES(bridge)[index][3]); /* set y multiplier */ spca50x_reg_write(spca50x->dev, 0, 0x8002, GET_EXT_MODES(bridge)[index][4]); /* use compressed mode, VGA, with mode specific subsample */ spca50x_reg_write(spca50x->dev, 0, 0x8003, ((GET_EXT_MODES(bridge)[index][2] & 0x0f) << 4)); /* enable drop packet */ if ((err = spca50x_reg_write(spca50x->dev, 0x00, 0x850a, 0x0001)) < 0) { PDEBUG(2, "failed to enable drop packet"); return err; } err = spca50x_reg_write(spca50x->dev, 0x00, 0x8880, 3); if (err < 0) { PDEBUG(2, "spca50x_reg_write failed"); return err; } err = spca50x_setup_qtable(spca50x, 0x00, 0x8800, 0x8840, qtable_creative_pccam); if (err < 0) { PDEBUG(2, "spca50x_setup_qtable failed"); return err; } //Init SDRAM - needed for SDRAM access spca50x_reg_write(spca50x->dev, 0x00, 0x870a, 0x04); /* switch to video camera mode */ err = spca50x_reg_write(spca50x->dev, 0x00, 0x8000, 0x0004); if (err < 0) { PDEBUG(2, "spca50x_reg_write camera mode failed"); return err; } if (spca50x_reg_readwait(spca50x->dev, 0, 0x8000, 0x44) != 0) { PDEBUG(2, "spca50x_reg_readwait() failed"); return -EIO; } usb_rd_vend_dev(spca50x->dev, 0, 0, 0x816b, &Data, 1); spca50x_reg_write(spca50x->dev, 0x00, 0x816b, Data); break; case CreativePCCam300: /* Creative PC-CAM 300 */ case IntelPocketPCCamera: /* FIXME: Temporary fix for Intel Pocket PC Camera - NWG (Sat 29th March 2003) */ /* do a full reset */ if ((err = spca500_full_reset(spca50x)) < 0) { PDEBUG(2, "spca500_full_reset failed"); return err; } if ((err = spca50x_reg_write(spca50x->dev, 0xe0, 0x0000, 0x0000)) < 0) { PDEBUG(2, "spca50x_reg_write() failed"); return err; } if ((err = spca50x_reg_readwait(spca50x->dev, 0x06, 0, 0)) < 0) { PDEBUG(2, "spca50x_reg_readwait() failed"); return err; } /* enable drop packet */ if ((err = spca50x_reg_write(spca50x->dev, 0x00, 0x850a, 0x0001)) < 0) { PDEBUG(2, "failed to enable drop packet"); return err; } err = spca50x_reg_write(spca50x->dev, 0x00, 0x8880, 3); if (err < 0) { PDEBUG(2, "spca50x_reg_write failed"); return err; } err = spca50x_setup_qtable(spca50x, 0x00, 0x8800, 0x8840, qtable_creative_pccam); if (err < 0) { PDEBUG(2, "spca50x_setup_qtable failed"); return err; } /* set x multiplier */ spca50x_reg_write(spca50x->dev, 0, 0x8001, GET_EXT_MODES(bridge)[index][3]); /* set y multiplier */ spca50x_reg_write(spca50x->dev, 0, 0x8002, GET_EXT_MODES(bridge)[index][4]); /* use compressed mode, VGA, with mode specific subsample */ spca50x_reg_write(spca50x->dev, 0, 0x8003, ((GET_EXT_MODES(bridge)[index][2] & 0x0f) << 4)); spca50x_reg_write(spca50x->dev, 0x20, 0x0001, 0x0004); /* switch to video camera mode */ err = spca50x_reg_write(spca50x->dev, 0x00, 0x8000, 0x0004); if (err < 0) { PDEBUG(2, "spca50x_reg_write camera mode failed"); return err; } if (spca50x_reg_readwait(spca50x->dev, 0, 0x8000, 0x44) != 0) { PDEBUG(2, "spca50x_reg_readwait() failed"); return -EIO; } usb_rd_vend_dev(spca50x->dev, 0, 0, 0x816b, &Data, 1); spca50x_reg_write(spca50x->dev, 0x00, 0x816b, Data); spca50x_write_vector(spca50x, spca500_visual_defaults); break; case KodakEZ200: /* Kodak EZ200 */ /* do a full reset */ if ((err = spca500_full_reset(spca50x)) < 0) { PDEBUG(2, "spca500_full_reset failed"); return err; } if ((err = spca50x_reg_write(spca50x->dev, 0xe0, 0x0000, 0x0000)) < 0) { PDEBUG(2, "spca50x_reg_write() failed"); return err; } if ((err = spca50x_reg_readwait(spca50x->dev, 0x06, 0, 0)) < 0) { PDEBUG(2, "spca50x_reg_readwait() failed"); return err; } /* enable drop packet */ if ((err = spca50x_reg_write(spca50x->dev, 0x00, 0x850a, 0x0001)) < 0) { PDEBUG(2, "failed to enable drop packet"); return err; } err = spca50x_reg_write(spca50x->dev, 0x00, 0x8880, 0); if (err < 0) { PDEBUG(2, "spca50x_reg_write failed"); return err; } err = spca50x_setup_qtable(spca50x, 0x00, 0x8800, 0x8840, qtable_kodak_ez200); if (err < 0) { PDEBUG(2, "spca50x_setup_qtable failed"); return err; } /* set x multiplier */ spca50x_reg_write(spca50x->dev, 0, 0x8001, GET_EXT_MODES(bridge)[index][3]); /* set y multiplier */ spca50x_reg_write(spca50x->dev, 0, 0x8002, GET_EXT_MODES(bridge)[index][4]); /* use compressed mode, VGA, with mode specific subsample */ spca50x_reg_write(spca50x->dev, 0, 0x8003, ((GET_EXT_MODES(bridge)[index][2] & 0x0f) << 4)); spca50x_reg_write(spca50x->dev, 0x20, 0x0001, 0x0004); /* switch to video camera mode */ err = spca50x_reg_write(spca50x->dev, 0x00, 0x8000, 0x0004); if (err < 0) { PDEBUG(2, "spca50x_reg_write camera mode failed"); return err; } if (spca50x_reg_readwait(spca50x->dev, 0, 0x8000, 0x44) != 0) { PDEBUG(2, "spca50x_reg_readwait() failed"); return -EIO; } usb_rd_vend_dev(spca50x->dev, 0, 0, 0x816b, &Data, 1); spca50x_reg_write(spca50x->dev, 0x00, 0x816b, Data); //spca50x_write_vector(spca50x, spca500_visual_defaults); break; case BenqDC1016: case DLinkDSC350: /* FamilyCam 300 */ case AiptekPocketDV: /* Aiptek PocketDV */ case Gsmartmini: /*Mustek Gsmart Mini */ case MustekGsmart300: // Mustek Gsmart 300 case PalmPixDC85: case Optimedia: case ToptroIndus: case AgfaCl20: spca500_reinit(spca50x); spca50x_reg_write(spca50x->dev, 0x00, 0x0d01, 0x01); /* enable drop packet */ spca50x_reg_write(spca50x->dev, 0x00, 0x850a, 0x0001); err = spca50x_setup_qtable(spca50x, 0x00, 0x8800, 0x8840, qtable_pocketdv); if (err < 0) { PDEBUG(2, "spca50x_setup_qtable failed"); return err; } spca50x_reg_write(spca50x->dev, 0x00, 0x8880, 2); /* familycam Quicksmart pocketDV stuff */ spca50x_reg_write(spca50x->dev, 0x00, 0x800a, 0x00); //Set agc transfer: synced inbetween frames spca50x_reg_write(spca50x->dev, 0x00, 0x820f, 0x01); //Init SDRAM - needed for SDRAM access spca50x_reg_write(spca50x->dev, 0x00, 0x870a, 0x04); /* set x multiplier */ spca50x_reg_write(spca50x->dev, 0, 0x8001, GET_EXT_MODES(bridge)[index][3]); /* set y multiplier */ spca50x_reg_write(spca50x->dev, 0, 0x8002, GET_EXT_MODES(bridge)[index][4]); /* use compressed mode, VGA, with mode specific subsample */ spca50x_reg_write(spca50x->dev, 0, 0x8003, ((GET_EXT_MODES(bridge)[index][2] & 0x0f) << 4)); /* switch to video camera mode */ err = spca50x_reg_write(spca50x->dev, 0x00, 0x8000, 0x0004); spca50x_reg_readwait(spca50x->dev, 0, 0x8000, 0x44); usb_rd_vend_dev(spca50x->dev, 0, 0, 0x816b, &Data, 1); spca50x_reg_write(spca50x->dev, 0x00, 0x816b, Data); break; case LogitechTraveler: case LogitechClickSmart510: { spca50x_reg_write(spca50x->dev, 0x02, 0x00, 0x00); /* enable drop packet */ if ((err = spca50x_reg_write(spca50x->dev, 0x00, 0x850a, 0x0001)) < 0) { PDEBUG(2, "failed to enable drop packet"); return err; } err = spca50x_setup_qtable(spca50x, 0x00, 0x8800, 0x8840, qtable_creative_pccam); if (err < 0) { PDEBUG(2, "spca50x_setup_qtable failed"); return err; } err = spca50x_reg_write(spca50x->dev, 0x00, 0x8880, 3); if (err < 0) { PDEBUG(2, "spca50x_reg_write failed"); return err; } spca50x_reg_write(spca50x->dev, 0x00, 0x800a, 0x00); //Init SDRAM - needed for SDRAM access spca50x_reg_write(spca50x->dev, 0x00, 0x870a, 0x04); /* set x multiplier */ spca50x_reg_write(spca50x->dev, 0, 0x8001, GET_EXT_MODES(bridge)[index][3]); /* set y multiplier */ spca50x_reg_write(spca50x->dev, 0, 0x8002, GET_EXT_MODES(bridge)[index][4]); /* use compressed mode, VGA, with mode specific subsample */ spca50x_reg_write(spca50x->dev, 0, 0x8003, ((GET_EXT_MODES(bridge)[index][2] & 0x0f) << 4)); /* switch to video camera mode */ err = spca50x_reg_write(spca50x->dev, 0x00, 0x8000, 0x0004); spca50x_reg_readwait(spca50x->dev, 0, 0x8000, 0x44); usb_rd_vend_dev(spca50x->dev, 0, 0, 0x816b, &Data, 1); spca50x_reg_write(spca50x->dev, 0x00, 0x816b, Data); spca50x_write_vector(spca50x, Clicksmart510_defaults); } break; default: return -EINVAL; } /* all ok */ return 0; } static int spca500_full_reset(struct usb_spca50x *spca50x) { int err; /* send the reset command */ err = spca50x_reg_write(spca50x->dev, 0xe0, 0x0001, 0x0000); if (err < 0) { return err; } /* wait for the reset to complete */ err = spca50x_reg_readwait(spca50x->dev, 0x06, 0x0000, 0x0000); if (err < 0) { return err; } /* all ok */ return 0; } /* * camera detection - handler for lookup in probe/match/attach. */ int spcaDetectCamera(int vendor, int product, struct pwc_softc *sc) { struct usb_spca50x *spca50x = &sc->spca50x; struct usb_device *dev = spca50x->dev; printf("%s called vend 0x%x prod 0x%x p %p\n", __FUNCTION__, vendor, product, spca50x); bzero(spca50x, sizeof(struct usb_spca50x)); bzero(&sc->pwc_info, sizeof(struct pwc_info)); bzero(&sc->camera_cb, sizeof(struct camera_callbacks)); spca50x->desc = UnknownCamera; /* this is the error case */ /* Initialize pwc_info and callbacks as appropriate */ sc->pwc_info.cb = &sc->camera_cb; sc->camera_cb = spca_callbacks; sc->pwc_info.devno.ud_vendor = vendor; sc->pwc_info.devno.ud_product = product; /* We switch on vendor and product, set spca50x->desc * if we find something. * Fields that are 0 can be left untouched. * The _CAM(desc, bridge, sensor, type) macro can help shorten the code. */ #define _CAM(_desc, _bridge, _sensor, _type) do { \ spca50x->desc = (_desc); \ sc->pwc_info.bridge = (_bridge); \ sc->pwc_info.sensor = (_sensor); \ spca50x->cameratype = (_type); \ } while (0) switch (vendor) { case 0xeb1a: /* silvercrest */ switch (product) { case 0x2710: case 0x2820: _CAM(SilverCrest, BRIDGE_EM2820, SENSOR_PAS202, JPEG); break; } break; case 0x0733: /* Rebadged ViewQuest (Intel) and ViewQuest cameras */ switch (product) { case 0x430: if (usbgrabber) { _CAM(UsbGrabberPV321c, BRIDGE_SPCA506, SENSOR_SAA7113, YYUV); spca50x->header_len = SPCA50X_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; info("USB SPCA5XX grabber found. Daemon PV321c(SPCA506+SAA7113)"); } else { _CAM(IntelPCCameraPro, BRIDGE_SPCA505, SENSOR_INTERNAL, YYUV); spca50x->header_len = SPCA50X_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; info("USB SPCA5XX camera found. Type Intel PC Camera Pro (SPCA505)"); } break; case 0x1314: _CAM(Mercury21, BRIDGE_SPCA533, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA533_OFFSET_DATA; info("USB SPCA5XX camera found. Mercury Digital Pro 2.1Mp "); break; case 0x2211: _CAM(Jenoptikjdc21lcd, BRIDGE_SPCA533, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA533_OFFSET_DATA; info("USB SPCA5XX camera found. Jenoptik JDC 21 LCD"); break; case 0x2221: _CAM(MercuryDigital, BRIDGE_SPCA533, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA533_OFFSET_DATA; info("USB SPCA5XX camera found. Mercury Digital Pro 3.1Mp "); break; case 0x1311: _CAM(Epsilon13, BRIDGE_SPCA533, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA533_OFFSET_DATA; info("USB SPCA5XX camera found. Digital Dream Epsilon 1.3"); break; case 0x401: _CAM(IntelCreateAndShare, BRIDGE_SPCA501, SENSOR_INTERNAL, YUYV); // BRIDGE_SPCA501 is a guess. At least the chip looks closer to the 501 than the 505 */ spca50x->header_len = SPCA501_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; info("USB SPCA5XX camera found. Type Intel Create and Share (SPCA501 )"); break; case 0x402: _CAM(ViewQuestM318B, BRIDGE_SPCA501, SENSOR_INTERNAL, YUYV); // BRIDGE_SPCA501; is a guess. At least the chip looks closer to the 501 than the 505 */ spca50x->header_len = SPCA501_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; info("USB SPCA5XX camera found. ViewQuest M318B (SPCA501a)"); /* Looks not perfectly but until we understand the difference between spca501 and spca500 we'll treat them as one */ break; case 0x110: _CAM(ViewQuestVQ110, BRIDGE_SPCA508, SENSOR_INTERNAL, YUVY); spca50x->header_len = SPCA508_OFFSET_DATA; spca50x->i2c_ctrl_reg = 0; spca50x->i2c_base = SPCA508_INDEX_I2C_BASE; spca50x->i2c_trigger_on_write = 1; info("USB SPCA5XX camera found. Type ViewQuest (SPCA508?)"); break; case 0x3261: _CAM(Concord3045, BRIDGE_SPCA536, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA536_OFFSET_DATA; info("USB SPCA5XX camera found.Concord 3045 Spca536 Mpeg4"); break; case 0x3281: _CAM(CyberpixS550V, BRIDGE_SPCA536, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA536_OFFSET_DATA; info("USB SPCA5XX camera found.Mercury Cyberpix Spca536 Mpeg4"); break; }; break; case 0x0734: switch (product) { case 0x043b: _CAM(DeMonUSBCapture, BRIDGE_SPCA506, SENSOR_SAA7113, YYUV); spca50x->header_len = SPCA50X_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; info("Detected DeMonUsbCapture (SPCA506+SAA7113)"); break; }; break; case 0x99FA: /* GrandTec cameras */ switch (product) { case 0x8988: _CAM(GrandtecVcap, BRIDGE_SPCA506, SENSOR_SAA7113, YYUV); spca50x->header_len = SPCA501_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; info("USB SPCA5XX camera found. Grandtec V.cap (SPCA506+SAA7113?)"); break; }; break; case 0x0AF9: /* Hama cameras */ switch (product) { case 0x0010: _CAM(HamaUSBSightcam, BRIDGE_SPCA508, SENSOR_INTERNAL, YUVY); spca50x->header_len = SPCA508_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; info("USB SPCA5XX camera found. Hama Sightcam 100 (SPCA508A+PAS106B)"); break; case 0x0011: _CAM(HamaUSBSightcam2, BRIDGE_SPCA508, SENSOR_INTERNAL, YUVY); spca50x->header_len = SPCA508_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; info("USB SPCA5XX camera found. Hama Sightcam 100 (2) (SPCA508A)"); break; }; break; case 0x040A: /* Kodak cameras */ switch (product) { case 0x0002: _CAM(KodakDVC325, BRIDGE_SPCA501, SENSOR_INTERNAL, YUYV); spca50x->header_len = SPCA501_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; info("USB SPCA5XX camera found. Type Kodak DVC-325 (SPCA501A )"); break; case 0x0300: _CAM(KodakEZ200, BRIDGE_SPCA500, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA500_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; info("USB SPCA5XX camera found. Type Kodak EZ200 (SPCA500+unknown CCD)"); break; }; break; case 0x04a5: /* Benq */ case 0x08ca: /* Aiptek */ case 0x055f: /* Mustek cameras */ case 0x04fc: /* SunPlus */ case 0x052b: /* ?? Megapix */ case 0x04f1: /* JVC */ switch (product) { case 0xc520: _CAM(MustekGsmartMini3, BRIDGE_SPCA504, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA50X_OFFSET_DATA; info("USB SPCA5XX camera found. Type Mustek gSmart Mini 3(SPCA504A)"); break; case 0xc420: _CAM(MustekGsmartMini2, BRIDGE_SPCA504, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA50X_OFFSET_DATA; info("USB SPCA5XX camera found. Type Mustek gSmart Mini 2(SPCA504A)"); break; case 0xc360: _CAM(MustekDV4000, BRIDGE_SPCA536, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA536_OFFSET_DATA; info("USB SPCA5XX camera found. Mustek DV4000 Spca536 Mpeg4"); break; case 0xc211: _CAM(Bs888e, BRIDGE_SPCA536, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA536_OFFSET_DATA; info("USB SPCA5XX camera found. Kowa Bs-888e Spca536 Mpeg4"); break; case 0xc005: // zc302 chips _CAM(Wcam300A, BRIDGE_ZC3XX, SENSOR_TAS5130C, JPGH); spca50x->header_len = SPCA50X_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; info("USB SPCA5XX camera found. Type Mustek Wcam300a Zc0301 "); break; case 0xd003: // zc302 chips _CAM(MustekWcam300A, BRIDGE_ZC3XX, SENSOR_TAS5130C, JPGH); spca50x->header_len = SPCA50X_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; info("USB SPCA5XX camera found. Type Mustek PCCam300a Zc0301 "); break; case 0xd004: // zc302 chips _CAM(WCam300AN, BRIDGE_ZC3XX, SENSOR_TAS5130C, JPGH); spca50x->header_len = SPCA50X_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; info("USB SPCA5XX camera found. Type Mustek WCam300aN Zc0302 "); break; case 0x504a: { __u8 fw = 0; /*try to get the firmware as some cam answer 2.0.1.2.2 and should be a spca504b then overwrite that setting */ usb_rd_vend_dev(dev, 0x20, 0, 0, &fw, 1); if (fw == 1) { _CAM(AiptekMiniPenCam13, BRIDGE_SPCA504, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA50X_OFFSET_DATA; info("USB SPCA5XX camera found. Type Aiptek mini PenCam 1.3(SPCA504A)"); } else if (fw == 2) { _CAM(Terratec2move13, BRIDGE_SPCA504B, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA50X_OFFSET_DATA; info("USB SPCA5XX camera found. Terratec 2 move1.3(SPCA504A FW2)"); } else return -ENODEV; } break; case 0x2018: _CAM(AiptekPenCamSD, BRIDGE_SPCA504B, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA50X_OFFSET_DATA; info("USB SPCA5XX camera found. Aiptek PenCam SD(SPCA504A FW2)"); break; case 0x1001: _CAM(JvcGcA50, BRIDGE_SPCA504B, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA50X_OFFSET_DATA; info("USB SPCA5XX camera found. JVC GC-A50(SPCA504A FW2)"); break; case 0x2008: _CAM(AiptekMiniPenCam2, BRIDGE_SPCA504B, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA50X_OFFSET_DATA; info("USB SPCA5XX camera found. Aiptek PenCam 2M(SPCA504A FW2)"); break; case 0x504b: _CAM(MaxellMaxPocket, BRIDGE_SPCA504B, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA50X_OFFSET_DATA; info("USB SPCA5XX camera found. Maxell MaxPocket 1.3 (SPCA504A FW2)"); break; case 0xffff: _CAM(PureDigitalDakota, BRIDGE_SPCA504B, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA50X_OFFSET_DATA; info("USB SPCA5XX camera found. Pure Digital Dakota (SPCA504A FW2)"); break; case 0x0103: _CAM(AiptekPocketDV, BRIDGE_SPCA500, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA500_OFFSET_DATA; info("USB SPCA5XX camera found. Aiptek PocketDV"); break; case 0x0104: _CAM(AiptekPocketDVII, BRIDGE_SPCA533, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA533_OFFSET_DATA; info("USB SPCA5XX camera found. Aiptek PocketDVII 1.3Mp"); break; case 0x0106: _CAM(AiptekPocketDV3100, BRIDGE_SPCA533, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA533_OFFSET_DATA; info("USB SPCA5XX camera found. Aiptek PocketDV3100+"); break; case 0xc630: _CAM(MustekMDC4000, BRIDGE_SPCA533, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA533_OFFSET_DATA; info("USB SPCA5XX camera found. Musteck MDC4000"); break; case 0x5330: _CAM(Digitrex2110, BRIDGE_SPCA533, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA533_OFFSET_DATA; info("USB SPCA5XX camera found. ApexDigital Digitrex 2110 spca533"); break; case 0x2020: _CAM(AiptekSlim3000F, BRIDGE_SPCA533, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA533_OFFSET_DATA; spca50x->cameratype = JPEG; info("USB SPCA5XX camera found type: Aiptek Slim3000F spca533"); break; case 0x2022: _CAM(AiptekSlim3200, BRIDGE_SPCA533, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA533_OFFSET_DATA; info("USB SPCA5XX camera found type: Aiptek Slim 3200 spca533"); break; case 0x2028: _CAM(AiptekPocketCam4M, BRIDGE_SPCA533, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA533_OFFSET_DATA; info("USB SPCA5XX camera found type: Aiptek PocketCam 4M spca533"); break; case 0x5360: _CAM(SunplusGeneric536, BRIDGE_SPCA536, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA536_OFFSET_DATA; info("USB SPCA5XX camera found. Aiptek Generic spca536a"); break; case 0x2024: _CAM(AiptekDV3500, BRIDGE_SPCA536, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA536_OFFSET_DATA; info("USB SPCA5XX camera found. Aiptek DV3500 Mpeg4"); break; case 0x2042: _CAM(AiptekPocketDV5100, BRIDGE_SPCA536, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA536_OFFSET_DATA; info("USB SPCA5XX camera found. Aiptek DV5100 Mpeg4"); break; case 0x2060: _CAM(AiptekPocketDV5300, BRIDGE_SPCA536, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA536_OFFSET_DATA; info("USB SPCA5XX camera found. Aiptek DV5300 Mpeg4"); break; case 0x3008: _CAM(BenqDC1500, BRIDGE_SPCA533, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA533_OFFSET_DATA; info("USB SPCA5XX camera found. Benq DC 1500 Spca533"); break; case 0x3003: _CAM(BenqDC1300, BRIDGE_SPCA504B, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA50X_OFFSET_DATA; info("USB SPCA5XX camera found. Benq DC 1300 Spca504b"); break; case 0x300a: _CAM(BenqDC3410, BRIDGE_SPCA533, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA533_OFFSET_DATA; info("USB SPCA5XX camera found. Benq DC 3410 Spca533"); break; case 0x300c: _CAM(BenqDC1016, BRIDGE_SPCA500, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA500_OFFSET_DATA; info("USB SPCA5XX camera found. Benq DC 1016 Spca500c "); break; case 0x2010: _CAM(AiptekPocketCam3M, BRIDGE_SPCA533, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA533_OFFSET_DATA; info("USB SPCA5XX camera found. Aiptek PocketCam 3M"); break; case 0x2016: _CAM(AiptekPocketCam2M, BRIDGE_SPCA504B, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA50X_OFFSET_DATA; info("USB SPCA5XX camera found. Aiptek PocketCam 2 Mega (SPCA504A FW2)"); break; case 0x0561: _CAM(Flexcam100Camera, BRIDGE_SPCA561, SENSOR_INTERNAL, S561); spca50x->header_len = SPCA561_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = SPCA561_INDEX_I2C_BASE; spca50x->i2c_trigger_on_write = 1; info("USB SPCA5XX camera found. Type Flexcam 100 (SPCA561A)"); break; case 0xc200: _CAM(MustekGsmart300, BRIDGE_SPCA500, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA500_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; info("USB SPCA5XX camera found. Mustek Gsmart 300"); break; case 0x7333: _CAM(PalmPixDC85, BRIDGE_SPCA500, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA500_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; info("USB SPCA5XX camera found. PalmPix DC85"); break; case 0xc220: _CAM(Gsmartmini, BRIDGE_SPCA500, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA500_OFFSET_DATA; info("USB SPCA5XX camera found. Mustek Gsmart Mini Spca500c "); break; case 0xc230: _CAM(Mustek330K, BRIDGE_SPCA533, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA533_OFFSET_DATA; info("USB SPCA5XX camera found. Mustek Digicam 330K "); break; case 0xc530: _CAM(MustekGsmartLCD3, BRIDGE_SPCA533, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA533_OFFSET_DATA; info("USB SPCA5XX camera found. Mustek Gsmart LCD 3"); break; case 0xc430: _CAM(MustekGsmartLCD2, BRIDGE_SPCA533, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA533_OFFSET_DATA; info("USB SPCA5XX camera found. Mustek Gsmart LCD 2"); break; case 0xc440: _CAM(MustekDV3000, BRIDGE_SPCA533, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA533_OFFSET_DATA; info("USB SPCA5XX camera found. DV3000"); break; case 0xc540: _CAM(GsmartD30, BRIDGE_SPCA533, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA533_OFFSET_DATA; info("USB SPCA5XX camera found.Mustek Gsmart D30"); break; case 0xc650: _CAM(MustekMDC5500Z, BRIDGE_SPCA533, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA533_OFFSET_DATA; info("USB SPCA5XX camera found Mustek MDC5500Z"); break; case 0x1513: _CAM(MegapixV4, BRIDGE_SPCA533, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA533_OFFSET_DATA; info("USB SPCA5XX camera found Megapix V4"); break; }; break; case 0x046d: /* Logitech Labtec */ case 0x041E: /* Creative cameras */ switch (product) { case 0x0870: /* Dexxa webcam */ _CAM(DexxaWebcam, BRIDGE_STV0602, 0 /* quickcam_configure */, PGBRG); if (qc_probe_sensor(sc) < 0) { printf("error configuring sensor\n"); spca50x->desc = UnknownCamera; break; } spca50x->alt = 1; /* interface 3 is biggest, use 1 though */ break; case 0x400A: _CAM(CreativePCCam300, BRIDGE_SPCA500, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA500_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; info("USB SPCA5XX camera found. Type Creative PC-CAM 300 (SPCA500+unknown CCD)"); break; case 0x4012: _CAM(PcCam350, BRIDGE_SPCA504C, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA504_PCCAM600_OFFSET_DATA;; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; info("USB SPCA5XX camera found. Type Creative PC-CAM 350 (SPCA504c+unknown CCD)"); break; case 0x0890: _CAM(LogitechTraveler, BRIDGE_SPCA500, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA500_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; info("USB SPCA5XX camera found. Logitech QuickCam Traveler (SPCA500+unknown CCD)"); break; case 0x08a0: _CAM(QCim, BRIDGE_ZC3XX, SENSOR_TAS5130C, JPGH); spca50x->header_len = SPCA50X_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; info("USB SPCA5XX camera found. Logitech QC IM "); break; case 0x08a1: _CAM(QCimA1, BRIDGE_ZC3XX, SENSOR_TAS5130C, JPGH); spca50x->header_len = SPCA50X_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; info("USB SPCA5XX camera found. Logitech QC IM "); break; case 0x08a2: // zc302 chips _CAM(LabtecPro, BRIDGE_ZC3XX, SENSOR_HDCS2020, JPGH); spca50x->header_len = SPCA50X_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; info("USB SPCA5XX camera found. Type Labtec Webcam Pro Zc0302 + Hdcs2020"); break; case 0x08a3: _CAM(QCchat, BRIDGE_ZC3XX, SENSOR_TAS5130C, JPGH); spca50x->header_len = SPCA50X_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; info("USB SPCA5XX camera found. Logitech QC Chat "); break; case 0x08a6: _CAM(LogitechQCim, BRIDGE_ZC3XX, SENSOR_HV7131C, JPGH); spca50x->header_len = SPCA50X_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; info("USB SPCA5XX camera found. Logitech QC IM "); break; case 0x08a9: _CAM(LogitechNotebookDeluxe, BRIDGE_ZC3XX, SENSOR_HDCS2020, JPGH); spca50x->header_len = SPCA50X_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; info("USB SPCA5XX camera found. Logitech Notebooks Deluxe Zc0302 + Hdcs2020"); break; case 0x08ae: _CAM(QuickCamNB, BRIDGE_ZC3XX, SENSOR_HDCS2020, JPGH); spca50x->header_len = SPCA50X_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; info("USB SPCA5XX camera found. Logitech QC for Notebooks "); break; case 0x08ad: _CAM(LogitechQCCommunicateSTX, BRIDGE_ZC3XX, SENSOR_HV7131C, JPGH); spca50x->header_len = SPCA50X_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; info("USB SPCA5XX camera found. Logitech QC Communicate STX "); break; case 0x08aa: _CAM(LabtecNotebook, BRIDGE_ZC3XX, SENSOR_HDCS2020, JPGH); spca50x->header_len = SPCA50X_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; info("USB SPCA5XX camera found. Labtec for Notebooks "); break; case 0x08b9: _CAM(QCimB9, BRIDGE_ZC3XX, SENSOR_TAS5130C, JPGH); spca50x->header_len = SPCA50X_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; info("USB SPCA5XX camera found. Logitech QC IM ??? "); break; case 0x08da: _CAM(QCmessenger, BRIDGE_ZC3XX, SENSOR_TAS5130C, JPGH); spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; break; case 0x0900: _CAM(LogitechClickSmart310, BRIDGE_SPCA500, SENSOR_HDCS1020, JPEG); spca50x->header_len = SPCA500_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; info("USB SPCA5XX camera found. Logitech ClickSmart 310 (SPCA551+ Agilent HDCS1020)"); break; case 0x0901: _CAM(LogitechClickSmart510, BRIDGE_SPCA500, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA500_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; info("USB SPCA5XX camera found. Logitech ClickSmart 510 (SPCA500+unknown CCD)"); break; case 0x0905: _CAM(LogitechClickSmart820, BRIDGE_SPCA533, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA533_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; info("USB SPCA5XX camera found. Logitech ClickSmart 820 (SPCA533+unknown CCD)"); break; case 0x400B: _CAM(CreativePCCam600, BRIDGE_SPCA504C, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA504_PCCAM600_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; info("USB SPCA5XX camera found. Type Creative PC-CAM 600 (SPCA504+unknown CCD)"); break; case 0x4013: _CAM(CreativePccam750, BRIDGE_SPCA504C, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA504_PCCAM600_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; info("USB SPCA5XX camera found. Type Creative PC-CAM 750 (SPCA504+unknown CCD)"); break; case 0x0960: _CAM(LogitechClickSmart420, BRIDGE_SPCA504C, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA504_PCCAM600_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; info("USB SPCA5XX camera found. Type Logitech Clicksmart 420 (SPCA504+unknown CCD)"); break; case 0x4018: _CAM(CreativeVista, BRIDGE_SPCA508, SENSOR_PB100_BA, YUVY); spca50x->header_len = SPCA50X_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; info("USB SPCA5XX camera found. Type Creative Vista (SPCA508A+PB100)"); break; case 0x4028: _CAM(CreativeVistaPlus, BRIDGE_PAC207, SENSOR_PAC207, PGBRG); spca50x->header_len = 12; spca50x->i2c_ctrl_reg = 0; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; info("USB SPCA5XX camera found. Type Creative Vista Plus (VF0090)"); break; case 0x401d: //here505b _CAM(Nxultra, BRIDGE_SPCA505, SENSOR_INTERNAL, YYUV); spca50x->header_len = SPCA50X_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; info("USB SPCA5XX camera found. Type Creative Webcam NX Ultra (SPCA505b+unknown CCD)"); break; case 0x401c: // zc301 chips _CAM(CreativeNX, BRIDGE_ZC3XX, SENSOR_PAS106, JPGH); spca50x->header_len = SPCA50X_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; info("USB SPCA5XX camera found. Type Creative NX Zc301+ CCD PAS106B"); break; case 0x401e: // zc301 chips _CAM(CreativeNxPro, BRIDGE_ZC3XX, SENSOR_HV7131B, JPGH); spca50x->header_len = SPCA50X_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; info("USB SPCA5XX camera found. Type Creative NX Pro Zc301+hv7131b"); break; case 0x4034: // zc301 chips _CAM(CreativeInstant1, BRIDGE_ZC3XX, SENSOR_PAS106, JPGH); spca50x->header_len = SPCA50X_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; info("USB SPCA5XX camera found. Type Creative Instant P0620"); break; case 0x4035: // zc301 chips _CAM(CreativeInstant2, BRIDGE_ZC3XX, SENSOR_PAS106, JPGH); spca50x->header_len = SPCA50X_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; info("USB SPCA5XX camera found. Type Creative Instant P0620D"); break; case 0x403a: _CAM(CreativeNxPro2, BRIDGE_ZC3XX, SENSOR_TAS5130C, JPGH); spca50x->header_len = SPCA50X_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; info("USB SPCA5XX camera found. Type Creative Nx Pro FW2 Zc301+Tas5130c"); break; case 0x403b: _CAM(CreativeVista3b, BRIDGE_SPCA561, SENSOR_INTERNAL, S561); spca50x->header_len = SPCA561_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = SPCA561_INDEX_I2C_BASE; spca50x->i2c_trigger_on_write = 1; info("USB SPCA5XX camera found. Creative Vista VF0010 (SPCA561A)"); break; #if 0 /* XXX fixme */ case 0x4036: spca50x->desc = CreativeLive; spca50x->bridge = BRIDGE_ZC3XX; spca50x->sensor = SENSOR_TAS5130C; spca50x->header_len = SPCA50X_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; spca50x->cameratype = JPGH; info("USB SPCA5XX camera found. Type Creative Live! Zc301+Tas5130c"); break; case 0x401f: // zc301 chips spca50x->desc = CreativeNotebook; spca50x->bridge = BRIDGE_ZC3XX; spca50x->sensor = SENSOR_TAS5130C; spca50x->header_len = SPCA50X_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; spca50x->cameratype = JPGH; info("USB SPCA5XX camera found. Type Creative Webcam Notebook Zc301+Tas5130c"); break; case 0x4017: // zc301 chips spca50x->desc = CreativeMobile; spca50x->bridge = BRIDGE_ZC3XX; spca50x->sensor = SENSOR_ICM105A; spca50x->header_len = SPCA50X_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; spca50x->cameratype = JPGH; info("USB SPCA5XX camera found. Type Creative Webcam Mobile Zc301+Icm105a"); break; case 0x0920: spca50x->desc = QCExpress; spca50x->bridge = BRIDGE_TV8532; spca50x->sensor = SENSOR_INTERNAL; spca50x->header_len = 4; spca50x->i2c_ctrl_reg = 0; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; spca50x->cameratype = GBGR; info("USB SPCA5xx camera found. Type QC Express (unknown CCD)"); break; case 0x0921: spca50x->desc = LabtecWebcam; spca50x->bridge = BRIDGE_TV8532; spca50x->sensor = SENSOR_INTERNAL; spca50x->header_len = 4; spca50x->i2c_ctrl_reg = 0; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; spca50x->cameratype = GBGR; info("USB SPCA5xx camera found. Type Labtec Webcam (unknown CCD)"); break; case 0x0928: spca50x->desc = QCExpressEtch2; spca50x->bridge = BRIDGE_SPCA561; spca50x->sensor = SENSOR_INTERNAL; spca50x->header_len = SPCA561_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = SPCA561_INDEX_I2C_BASE; spca50x->i2c_trigger_on_write = 1; spca50x->cameratype = S561; info("USB SPCA5XX camera found.Logitech QuickCam Express II(SPCA561A)"); break; case 0x0929: spca50x->desc = Labtec929; spca50x->bridge = BRIDGE_SPCA561; spca50x->sensor = SENSOR_INTERNAL; spca50x->header_len = SPCA561_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = SPCA561_INDEX_I2C_BASE; spca50x->i2c_trigger_on_write = 1; spca50x->cameratype = S561; info("USB SPCA5XX camera found.Labtec WebCam Elch 2(SPCA561A)"); break; case 0x092a: spca50x->desc = QCforNotebook; spca50x->bridge = BRIDGE_SPCA561; spca50x->sensor = SENSOR_INTERNAL; spca50x->header_len = SPCA561_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = SPCA561_INDEX_I2C_BASE; spca50x->i2c_trigger_on_write = 1; spca50x->cameratype = S561; info("USB SPCA5XX camera found.Logitech QuickCam for Notebook (SPCA561A)"); break; case 0x092b: spca50x->desc = LabtecWCPlus; spca50x->bridge = BRIDGE_SPCA561; spca50x->sensor = SENSOR_INTERNAL; spca50x->header_len = SPCA561_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = SPCA561_INDEX_I2C_BASE; spca50x->i2c_trigger_on_write = 1; spca50x->cameratype = S561; info("USB SPCA5XX camera found.Labtec Webcam Plus (SPCA561A)"); break; case 0x092c: spca50x->desc = LogitechQC92c; spca50x->bridge = BRIDGE_SPCA561; spca50x->sensor = SENSOR_INTERNAL; spca50x->header_len = SPCA561_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = SPCA561_INDEX_I2C_BASE; spca50x->i2c_trigger_on_write = 1; spca50x->cameratype = S561; info("USB SPCA5XX camera found.Logitech QuickCam chat (SPCA561A)"); break; #endif /* XXX fixme */ }; break; #if 0 /* XXX fixme */ case 0x0AC8: /* Vimicro z-star */ switch (product) { case 0x301b: /* Wasam 350r */ spca50x->desc = Vimicro; spca50x->bridge = BRIDGE_ZC3XX; spca50x->sensor = SENSOR_PB0330; //overwrite by the sensor detect routine spca50x->header_len = SPCA50X_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; spca50x->cameratype = JPGH; info("USB SPCA5XX camera found. Type Vimicro Zc301P 0x301b"); break; case 0x303b: /* Wasam 350r */ spca50x->desc = Vimicro303b; spca50x->bridge = BRIDGE_ZC3XX; spca50x->sensor = SENSOR_PB0330; //overwrite by the sensor detect routine spca50x->header_len = SPCA50X_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; spca50x->cameratype = JPGH; info("USB SPCA5XX camera found. Type Vimicro Zc301P 0x303b"); break; case 0x305b: /* Generic */ spca50x->desc = Zc0305b; spca50x->bridge = BRIDGE_ZC3XX; spca50x->sensor = SENSOR_TAS5130C; //overwrite by the sensor detect routine spca50x->header_len = SPCA50X_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; spca50x->cameratype = JPGH; info("USB SPCA5XX camera found. Type Vimicro Zc305B 0x305b"); break; case 0x0302: /* Generic */ spca50x->desc = Zc302; spca50x->bridge = BRIDGE_ZC3XX; spca50x->sensor = SENSOR_ICM105A; //overwrite by the sensor detect routine spca50x->header_len = SPCA50X_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; spca50x->cameratype = JPGH; info("USB SPCA5XX camera found. Type Vimicro Zc302 "); break; }; break; case 0x084D: /* D-Link / Minton */ switch (product) { case 0x0003: /* DSC-350 / S-Cam F5 */ spca50x->desc = DLinkDSC350; spca50x->bridge = BRIDGE_SPCA500; spca50x->sensor = SENSOR_INTERNAL; spca50x->header_len = SPCA500_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; spca50x->cameratype = JPEG; info("USB SPCA5XX camera found. Type D-Link DSC-350 / Minton S-Cam F5 (SPCA500+unknown CCD)"); break; }; break; case 0x0923: /* ICM532 cams */ switch (product) { case 0x010f: spca50x->desc = ICM532cam; spca50x->bridge = BRIDGE_TV8532; spca50x->sensor = SENSOR_INTERNAL; spca50x->header_len = 4; spca50x->cameratype = GBGR; info("USB SPCA5xx camera found. Type ICM532 cam (unknown CCD)"); break; }; break; case 0x0545: /* tv8532 cams */ switch (product) { case 0x808b: spca50x->desc = VeoStingray2; spca50x->bridge = BRIDGE_TV8532; spca50x->sensor = SENSOR_INTERNAL; spca50x->header_len = 4; spca50x->cameratype = GBGR; info("USB SPCA5xx camera found. Type Veo Stingray (unknown CCD)"); break; case 0x8333: spca50x->desc = VeoStingray1; spca50x->bridge = BRIDGE_TV8532; spca50x->sensor = SENSOR_INTERNAL; spca50x->header_len = 4; spca50x->cameratype = GBGR; info("USB SPCA5xx camera found. Type Veo Stingray (unknown CCD)"); break; }; break; case 0x102c: /* Etoms */ switch (product) { case 0x6151: spca50x->desc = Etoms61x151; spca50x->bridge = BRIDGE_ETOMS; spca50x->sensor = SENSOR_PAS106; spca50x->cameratype = GBRG; info("USB Etx61xx51 camera found.Qcam Sangha Et61x151+Pas 106 "); break; case 0x6251: spca50x->desc = Etoms61x251; spca50x->bridge = BRIDGE_ETOMS; spca50x->sensor = SENSOR_TAS5130C; spca50x->cameratype = GBRG; info("USB Etx61xx51 camera found.Qcam xxxxxx Et61x251+Tas 5130c"); break; }; break; case 0x1776: /* Arowana */ switch (product) { case 0x501c: /* Arowana 300k CMOS Camera */ spca50x->desc = Arowana300KCMOSCamera; spca50x->bridge = BRIDGE_SPCA501; spca50x->sensor = SENSOR_HV7131B; spca50x->header_len = SPCA501_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; spca50x->cameratype = YUYV; info("USB SPCA5XX camera found. Type Arowana 300k CMOS Camera (SPCA501C+HV7131B)"); break; }; break; case 0x0000: /* Unknow Camera */ switch (product) { case 0x0000: /* UnKnow from Ori CMOS Camera */ spca50x->desc = MystFromOriUnknownCamera; spca50x->bridge = BRIDGE_SPCA501; spca50x->sensor = SENSOR_HV7131B; spca50x->header_len = SPCA501_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; spca50x->cameratype = YUYV; info("USB SPCA5XX camera found. UnKnow CMOS Camera (SPCA501C+HV7131B)"); break; }; break; case 0x8086: /* Intel */ switch (product) { case 0x0110: spca50x->desc = IntelEasyPCCamera; spca50x->bridge = BRIDGE_SPCA508; spca50x->sensor = SENSOR_PB100_BA; spca50x->header_len = SPCA508_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = SPCA508_INDEX_I2C_BASE; spca50x->i2c_trigger_on_write = 1; spca50x->cameratype = YUVY; info("USB SPCA5XX camera found. Type Intel Easy PC Camera CS110 (SPCA508+PB100)"); break; case 0x0630: /* Pocket PC Camera */ spca50x->desc = IntelPocketPCCamera; spca50x->bridge = BRIDGE_SPCA500; spca50x->sensor = SENSOR_INTERNAL; spca50x->header_len = SPCA500_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; spca50x->cameratype = JPEG; info("USB SPCA5XX camera found. Type Intel Pocket PC Camera (SPCA500+unknown CCD)"); break; }; break; case 0x0506: /* 3COM cameras */ switch (product) { case 0x00DF: spca50x->desc = ThreeComHomeConnectLite; spca50x->bridge = BRIDGE_SPCA501; spca50x->sensor = SENSOR_INTERNAL; spca50x->header_len = SPCA501_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; spca50x->cameratype = YUYV; info("USB SPCA5XX camera found. Type 3Com HomeConnect Lite (SPCA501A+?)"); break; }; break; case 0x0458: /* Genius KYE cameras */ switch (product) { case 0x7004: spca50x->desc = GeniusVideoCAMExpressV2; spca50x->bridge = BRIDGE_SPCA561; spca50x->sensor = SENSOR_INTERNAL; spca50x->header_len = SPCA561_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = SPCA561_INDEX_I2C_BASE; spca50x->i2c_trigger_on_write = 1; spca50x->cameratype = S561; info("USB SPCA5XX camera found. Type Genius VideoCAM Express V2 (SPCA561A)"); break; case 0x7006: spca50x->desc = GeniusDsc13; spca50x->bridge = BRIDGE_SPCA504B; spca50x->sensor = SENSOR_INTERNAL; spca50x->header_len = SPCA50X_OFFSET_DATA; spca50x->i2c_ctrl_reg = 0; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; spca50x->cameratype = JPEG; info("USB SPCA5XX camera found. Type Genius DSC 1.3 Smart Spca504B"); break; case 0x7007: // zc301 chips spca50x->desc = GeniusVideoCamV2; spca50x->bridge = BRIDGE_ZC3XX; spca50x->sensor = SENSOR_TAS5130C; spca50x->header_len = SPCA50X_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; spca50x->cameratype = JPGH; info("USB SPCA5XX camera found. Type Genius VideoCam V2 Zc301+Tas5130c"); break; case 0x700c: // zc301 chips spca50x->desc = GeniusVideoCamV3; spca50x->bridge = BRIDGE_ZC3XX; spca50x->sensor = SENSOR_TAS5130C; spca50x->header_len = SPCA50X_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; spca50x->cameratype = JPGH; info("USB SPCA5XX camera found. Type Genius VideoCam V3 Zc301+Tas5130c"); break; case 0x700f: // zc301 chips spca50x->desc = GeniusVideoCamExpressV2b; spca50x->bridge = BRIDGE_ZC3XX; spca50x->sensor = SENSOR_TAS5130C; spca50x->header_len = SPCA50X_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; spca50x->cameratype = JPGH; info("USB SPCA5XX camera found. Type Genius VideoCam Express V2 Zc301+Tas5130c"); break; }; break; case 0xabcd: /* PetCam */ switch (product) { case 0xcdee: spca50x->desc = PetCam; spca50x->bridge = BRIDGE_SPCA561; spca50x->sensor = SENSOR_INTERNAL; spca50x->header_len = SPCA561_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = SPCA561_INDEX_I2C_BASE; spca50x->i2c_trigger_on_write = 1; spca50x->cameratype = S561; info("USB SPCA5XX camera found. Type Petcam (SPCA561A)"); break; }; break; case 0x060b: /* Maxell */ switch (product) { case 0xa001: spca50x->desc = MaxellCompactPM3; spca50x->bridge = BRIDGE_SPCA561; spca50x->sensor = SENSOR_INTERNAL; spca50x->header_len = SPCA561_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = SPCA561_INDEX_I2C_BASE; spca50x->i2c_trigger_on_write = 1; spca50x->cameratype = S561; info("USB SPCA5XX camera found. Type Maxell Compact PCPM3 (SPCA561A)"); break; }; break; case 0x10fd: /* FlyCam usb 100 */ switch (product) { case 0x7e50: spca50x->desc = Flycam100Camera; spca50x->bridge = BRIDGE_SPCA561; spca50x->sensor = SENSOR_INTERNAL; spca50x->header_len = SPCA561_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = SPCA561_INDEX_I2C_BASE; spca50x->i2c_trigger_on_write = 1; spca50x->cameratype = S561; info("USB SPCA5XX camera found. FlyCam Usb100 (SPCA561A)"); break; case 0x0128: case 0x8050: // zc301 chips spca50x->desc = TyphoonWebshotIIUSB300k; spca50x->bridge = BRIDGE_ZC3XX; spca50x->sensor = SENSOR_TAS5130C; spca50x->header_len = SPCA50X_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; spca50x->cameratype = JPGH; info("USB SPCA5XX camera found. Type Typhoon Webshot II Zc301p Tas5130c"); break; }; break; case 0x0461: /* MicroInnovation */ switch (product) { case 0x0815: spca50x->desc = MicroInnovationIC200; spca50x->bridge = BRIDGE_SPCA508; spca50x->sensor = SENSOR_PB100_BA; spca50x->header_len = SPCA508_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = SPCA508_INDEX_I2C_BASE; spca50x->i2c_trigger_on_write = 1; spca50x->cameratype = YUVY; info("USB SPCA5XX camera found. Type MicroInnovation IC200 (SPCA508+PB100)"); break; case 0x0a00: // zc301 chips spca50x->desc = WebCam320; spca50x->bridge = BRIDGE_ZC3XX; spca50x->sensor = SENSOR_TAS5130C; spca50x->header_len = SPCA50X_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; spca50x->cameratype = JPGH; info("USB SPCA5XX camera found. Type Micro Innovation PC Cam 300A Zc301"); break; }; break; case 0x06e1: /* ADS Technologies */ switch (product) { case 0xa190: spca50x->desc = ADSInstantVCD; spca50x->bridge = BRIDGE_SPCA506; spca50x->sensor = SENSOR_SAA7113; spca50x->header_len = SPCA50X_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; //SPCA508_INDEX_I2C_BASE; spca50x->i2c_trigger_on_write = 0; spca50x->cameratype = YYUV; info("USB SPCA5XX camera found. Type ADS Instant VCD (SPCA506+SAA7113)"); break; }; break; case 0x05da: /* Digital Dream cameras */ switch (product) { case 0x1018: spca50x->desc = Enigma13; spca50x->bridge = BRIDGE_SPCA504B; spca50x->sensor = SENSOR_INTERNAL; spca50x->header_len = SPCA50X_OFFSET_DATA; spca50x->i2c_ctrl_reg = 0; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; spca50x->cameratype = JPEG; info("USB SPCA5XX camera found. Digital Dream Enigma 1.3"); break; }; break; case 0x0c45: /* Sonix6025 TAS 5130d1b */ switch (product) { case 0x6001: _CAM(GeniusVideoCamNB, BRIDGE_SONIX, SENSOR_TAS5110, SN9C); spca50x->customid = SN9C102; spca50x->i2c_ctrl_reg = 0x20; spca50x->i2c_base = 0x11; spca50x->i2c_trigger_on_write = 0; info("USB Genius VideoCAM NB found. SONIX sn9c102 + Tas 5110 "); break; case 0x6007: case 0x6005: spca50x->desc = SweexTas5110; spca50x->bridge = BRIDGE_SONIX; spca50x->sensor = SENSOR_TAS5110; spca50x->customid = SN9C101; spca50x->i2c_ctrl_reg = 0x20; spca50x->i2c_base = 0x11; spca50x->i2c_trigger_on_write = 0; spca50x->cameratype = SN9C; info("USB SPCA5XX camera found. SONIX sn9c101 +Tas 5110 "); break; case 0x6024: case 0x6025: spca50x->desc = Sonix6025; spca50x->bridge = BRIDGE_SONIX; spca50x->sensor = SENSOR_TAS5130C; spca50x->customid = SN9C102; spca50x->i2c_ctrl_reg = 0x20; spca50x->i2c_base = 0x11; spca50x->i2c_trigger_on_write = 0; spca50x->cameratype = SN9C; info("USB SPCA5XX camera found. SONIX sn9c102 +Tas 5130d1b "); break; case 0x6028: spca50x->desc = BtcPc380; spca50x->bridge = BRIDGE_SONIX; spca50x->sensor = SENSOR_PAS202; spca50x->customid = SN9C102; spca50x->i2c_ctrl_reg = 0x80; spca50x->i2c_base = 0x40; spca50x->i2c_trigger_on_write = 0; spca50x->cameratype = SN9C; info("USB SPCA5XX camera found. SONIX sn9c102 + Pas202"); break; case 0x6019: spca50x->desc = Sonix6019; spca50x->bridge = BRIDGE_SONIX; spca50x->sensor = SENSOR_OV7630; spca50x->customid = SN9C101; spca50x->i2c_ctrl_reg = 0x80; spca50x->i2c_base = 0x21; spca50x->i2c_trigger_on_write = 0; spca50x->cameratype = SN9C; info("USB SPCA5XX camera found. SONIX sn9c101 + Ov7630 "); break; case 0x602c: case 0x602e: spca50x->desc = GeniusVideoCamMessenger; spca50x->bridge = BRIDGE_SONIX; spca50x->sensor = SENSOR_OV7630; spca50x->customid = SN9C102; spca50x->i2c_ctrl_reg = 0x80; spca50x->i2c_base = 0x21; spca50x->i2c_trigger_on_write = 0; spca50x->cameratype = SN9C; info("USB SPCA5XX camera found. SONIX sn9c102 + Ov7630 "); break; case 0x602d: spca50x->desc = Lic200; spca50x->bridge = BRIDGE_SONIX; spca50x->sensor = SENSOR_HV7131R; spca50x->customid = SN9C102; spca50x->i2c_ctrl_reg = 0x80; spca50x->i2c_base = 0x11; spca50x->i2c_trigger_on_write = 0; spca50x->cameratype = SN9C; info("USB SPCA5XX camera found. SONIX sn9c102 Hv7131R "); break; case 0x6009: case 0x600d: case 0x6029: spca50x->desc = Sonix6029; spca50x->bridge = BRIDGE_SONIX; spca50x->sensor = SENSOR_PAS106; spca50x->customid = SN9C101; spca50x->i2c_ctrl_reg = 0x81; spca50x->i2c_base = 0x40; spca50x->i2c_trigger_on_write = 0; spca50x->cameratype = SN9C; //GBRG ; info("USB SPCA5XX camera found. SONIX sn9c102 + Pas106 "); break; case 0x6040: spca50x->desc = SpeedNVC350K; spca50x->bridge = BRIDGE_SN9CXXX; spca50x->sensor = SENSOR_HV7131R; spca50x->customid = SN9C102P; spca50x->i2c_ctrl_reg = 0x81; spca50x->i2c_base = 0x11; spca50x->i2c_trigger_on_write = 0; spca50x->cameratype = JPGS; // jpeg 4.2.2 whithout header ; info("USB SPCA5XX camera found. Speed NVC 350K sn9c102p + Hv7131R "); break; case 0x607c: spca50x->desc = SonixWC311P; spca50x->bridge = BRIDGE_SN9CXXX; spca50x->sensor = SENSOR_HV7131R; spca50x->customid = SN9C102P; spca50x->i2c_ctrl_reg = 0x81; spca50x->i2c_base = 0x11; spca50x->i2c_trigger_on_write = 0; spca50x->cameratype = JPGS; // jpeg 4.2.2 whithout header ; info("USB SPCA5XX camera found. SONIX sn9c102p + Hv7131R "); break; case 0x613c: spca50x->desc = Pccam168; spca50x->bridge = BRIDGE_SN9CXXX; spca50x->sensor = SENSOR_HV7131R; spca50x->customid = SN9C120; spca50x->i2c_ctrl_reg = 0x81; spca50x->i2c_base = 0x11; spca50x->i2c_trigger_on_write = 0; spca50x->cameratype = JPGS; // jpeg 4.2.2 whithout header ; info("USB SPCA5XX camera found. SONIX sn9c120 + Hv7131R "); break; case 0x6130: spca50x->desc = Pccam; spca50x->bridge = BRIDGE_SN9CXXX; spca50x->sensor = SENSOR_MI0360; spca50x->customid = SN9C120; spca50x->i2c_ctrl_reg = 0x81; spca50x->i2c_base = 0x5d; spca50x->i2c_trigger_on_write = 0; spca50x->cameratype = JPGS; // jpeg 4.2.2 whithout header ; info("USB SPCA5XX camera found. SONIX sn9c120 + MI0360 "); break; case 0x60c0: spca50x->desc = Sn535; spca50x->bridge = BRIDGE_SN9CXXX; spca50x->sensor = SENSOR_MI0360; spca50x->customid = SN9C105; spca50x->i2c_ctrl_reg = 0x81; spca50x->i2c_base = 0x5d; spca50x->i2c_trigger_on_write = 0; spca50x->cameratype = JPGS; // jpeg 4.2.2 whithout header ; info("USB SPCA5XX camera found. SONIX sn9c105 + MI0360 "); break; case 0x60fc: spca50x->desc = Lic300; spca50x->bridge = BRIDGE_SN9CXXX; spca50x->sensor = SENSOR_HV7131R; spca50x->customid = SN9C105; spca50x->i2c_ctrl_reg = 0x81; spca50x->i2c_base = 0x11; spca50x->i2c_trigger_on_write = 0; spca50x->cameratype = JPGS; // jpeg 4.2.2 whithout header ; info("USB SPCA5XX camera found. SONIX sn9c105 + HV7131R "); break; #if 0 case 0x624f: /* * XXX experimental entry for the Trust WB5500 * http://www.trust.nl/products/product.aspx?artnr=14830 * The manufacturer says 1.3mpix resolution. Windows .inf file: SBPCamDesc% = SN.USBPCam,USB\VID_0c45&PID_624f ; SN9C201 + OV9650 Also ASUS WebCam, 1.3M, USB2.0, FF */ _CAM(TrustWB5500, BRIDGE_SN9CXXX, SENSOR_HV7131R, JPGS); spca50x->customid = SN9C105; /* XXX 0x81 0x11 don't work */ spca50x->i2c_ctrl_reg = 0x81; spca50x->i2c_base = 0x5d; spca50x->i2c_trigger_on_write = 0; break; #endif }; break; #endif /* XXX fixme */ case 0x0546: /* Polaroid */ switch (product) { case 0x3273: _CAM(PolaroidPDC2030, BRIDGE_SPCA504B, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA50X_OFFSET_DATA; info("USB SPCA5XX camera found. Polaroid PDC 2030"); break; case 0x3155: _CAM(PolaroidPDC3070, BRIDGE_SPCA533, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA533_OFFSET_DATA; info("USB SPCA5XX camera found. Polaroid PDC 3070"); break; case 0x3191: _CAM(PolaroidIon80, BRIDGE_SPCA504B, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA50X_OFFSET_DATA; info("USB SPCA5XX camera found.Polaroid Ion80 (SPCA504A FW2)"); break; }; break; case 0x0572: /* Connexant */ switch (product) { case 0x0041: _CAM(CreativeNoteBook2, BRIDGE_CX11646, SENSOR_INTERNAL, JPGC); info("USB SPCA5XX camera found. Creative NoteBook PD1170"); break; }; break; case 0x06be: /* Optimedia */ switch (product) { case 0x0800: _CAM(Optimedia, BRIDGE_SPCA500, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA500_OFFSET_DATA; info("USB SPCA5XX camera found. Optimedia spca500a"); break; }; break; case 0x2899: /* ToptroIndustrial */ switch (product) { case 0x012c: _CAM(ToptroIndus, BRIDGE_SPCA500, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA500_OFFSET_DATA; info("USB SPCA5XX camera found. Toptro Industrial spca500a"); break; }; break; case 0x06bd: /* Agfa Cl20 */ switch (product) { case 0x0404: _CAM(AgfaCl20, BRIDGE_SPCA500, SENSOR_INTERNAL, JPEG); spca50x->header_len = SPCA500_OFFSET_DATA; info("USB SPCA5XX camera found. Agfa ephoto CL20 spca500a"); break; }; break; case 0x093a: /* Mars-semi ~ Pixart */ switch (product) { case 0x050f: _CAM(Pcam, BRIDGE_MR97311, SENSOR_MI0360, JPGM); spca50x->header_len = 16; info("USB SPCA5XX camera found. Mars-Semi MR97311 MI0360 "); break; case 0x2460: _CAM(QtecWb100, BRIDGE_PAC207, SENSOR_PAC207, PGBRG); spca50x->header_len = 12; info("USB SPCA5XX camera found. Qtec Webcam 100 Pac207-BCA "); break; case 0x2468: _CAM(PAC207, BRIDGE_PAC207, SENSOR_PAC207, PGBRG); spca50x->header_len = 12; /* XXX unused ? */ info("USB SPCA5XX camera found. Pixart PAC207BCA"); break; case 0x2470: _CAM(GeniusGF112, BRIDGE_PAC207, SENSOR_PAC207, PGBRG); spca50x->header_len = 12; info("USB SPCA5XX camera found.Genius GF112 (PAC207-BCA)"); break; case 0x2471: _CAM(GeniusGe111, BRIDGE_PAC207, SENSOR_PAC207, PGBRG); spca50x->header_len = 12; info("USB SPCA5XX camera found. Genius VideoCam Ge111"); break; }; break; case 0x0497: /* Smile International */ switch (product) { case 0xc001: // Modal NO. VA30UC2 8/nq h0 106250 // Hone-Tec Inc. VA30UC2 _CAM(SmileIntlCamera, BRIDGE_SPCA501, SENSOR_INTERNAL, YUYV); spca50x->header_len = SPCA501_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; info("USB SPCA5XX camera found Type Smile International "); break; }; break; case 0x0698: /* Chuntex (CTX) */ switch (product) { case 0x2003: /* The Webcam built in the CTX M730V TFT-Display, behind an USB-HUB */ _CAM(CTXM730VCam, BRIDGE_ZC3XX, SENSOR_ICM105A, JPGH); // SENSOR_ICM105A; //overwrite by the sensor detect routine spca50x->header_len = SPCA50X_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; info("USB SPCA5XX camera found. Type CTX M730V built in Webcam"); break; }; break; case 0x0471: /* Philips Product */ switch (product) { case 0x0325: /* Low cost Philips Webcam */ _CAM(PhilipsSPC200NC, BRIDGE_ZC3XX, SENSOR_PAS106, JPGH); // SENSOR_PAS106 overwrite by the sensor detect routine spca50x->header_len = SPCA50X_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; info("USB SPCA5XX camera found.Type Philips SPC200NC Vimicro PAS106"); break; case 0x0326: /* Low cost Philips Webcam */ _CAM(PhilipsSPC300NC, BRIDGE_ZC3XX, SENSOR_PAS106, JPGH); // SENSOR_PAS106; //overwrite by the sensor detect routine spca50x->header_len = SPCA50X_OFFSET_DATA; spca50x->i2c_ctrl_reg = SPCA50X_REG_I2C_CTRL; spca50x->i2c_base = 0; spca50x->i2c_trigger_on_write = 0; info("USB SPCA5XX camera found.Type Philips SPC300NC Vimicro ????"); break; case 0x0328: _CAM(PhilipsSPC700NC, BRIDGE_SN9CXXX, SENSOR_MI0360, JPGS); // JPGS; // jpeg 4.2.2 whithout header ; spca50x->customid = SN9C105; spca50x->i2c_ctrl_reg = 0x81; spca50x->i2c_base = 0x5d; spca50x->i2c_trigger_on_write = 0; info("USB SPCA5XX camera found. Philips SPC700NC sn9c105 + MI0360 "); break; case 0x0327: _CAM(PhilipsSPC600NC, BRIDGE_SN9CXXX, SENSOR_MI0360, JPGS); spca50x->customid = SN9C105; spca50x->i2c_ctrl_reg = 0x81; spca50x->i2c_base = 0x5d; spca50x->i2c_trigger_on_write = 0; info("USB SPCA5XX camera found. Philips SPC600NC sn9c105 + MI0360 "); break; }; break; } if (spca50x->desc == UnknownCamera) { printf("spcaCameraDetect failed\n"); return -ENODEV; } /* copy into the main descriptor */ sc->pwc_info.name = camera_name(sc->spca50x.desc); sc->pwc_info.dataformat = sc->spca50x.cameratype; /* XXX not sure if this depends on the bridge or on the sensor */ switch(sc->pwc_info.bridge) { case BRIDGE_ZC3XX: sc->camera_cb.cb_consume = zc3xx_consume; break; case BRIDGE_PAC207: sc->camera_cb.cb_consume = pac207_consume; break; case BRIDGE_SPCA561: sc->camera_cb.cb_consume = spca561_consume; break; case BRIDGE_STV0602: sc->camera_cb.cb_consume = qc_consume; break; } return 0; } //eof pwcbsd/spca5xx-20060402/drivers/usb/spca5xx.h000755 000423 000000 00000036653 10553461761 021127 0ustar00luigiwheel000000 000000 #ifndef SPCA50X_H #define SPCA50X_H /* * Header file for SPCA50x based camera driver. Originally copied from ov511 driver. * Originally by Mark W. McClelland * SPCA50x version by Joel Crisp; all bugs are mine, all nice features are his. */ #ifdef __KERNEL__ #if !defined(__FreeBSD__) #include #include #include #include #include #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20) && LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) #define urb_t struct urb #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20) */ #endif /* !__FreeBSD__ */ //static const char SPCA50X_H_CVS_VERSION[]="$Id: spca5xx.h,v 1.21 2007/01/17 16:43:06 luigi Exp $"; /* V4L API extension for raw JPEG (=JPEG without header) and JPEG with header */ #define VIDEO_PALETTE_RAW_JPEG 20 #define VIDEO_PALETTE_JPEG 21 #if defined(__FreeBSD__) # define PDEBUG(level, fmt, args...) \ if (pwcdebug >= level) printf("[%s:%d] " fmt "\n", __PRETTY_FUNCTION__, __LINE__ , ## args) #elif defined(SPCA50X_ENABLE_DEBUG) # define PDEBUG(level, fmt, args...) \ if (debug >= level) info("[%s:%d] " fmt "\n", __PRETTY_FUNCTION__, __LINE__ , ## args) #else /* SPCA50X_ENABLE_DEBUG */ # define PDEBUG(level, fmt, args...) do {} while(0) #endif /* SPCA50X_ENABLE_DEBUG */ //#define FRAMES_PER_DESC 10 /* Default value, should be reasonable */ #define FRAMES_PER_DESC 16 /* Default value, should be reasonable */ #define MAX_FRAME_SIZE_PER_DESC 1024 #define SPCA50X_MAX_WIDTH 640 #define SPCA50X_MAX_HEIGHT 480 #define SPCA50X_ENDPOINT_ADDRESS 1 /* Isoc endpoint number */ #define PAC207_ENDPOINT_ADDRESS 5 /* Isoc endpoint number */ /* only 2 or 4 frames are allowed here !!! */ #define SPCA50X_NUMFRAMES 2 #define SPCA50X_NUMSBUF 2 /* Alternate interface transfer sizes */ #define SPCA50X_ALT_SIZE_0 0 #define SPCA50X_ALT_SIZE_128 1 #define SPCA50X_ALT_SIZE_256 1 #define SPCA50X_ALT_SIZE_384 2 #define SPCA50X_ALT_SIZE_512 3 #define SPCA50X_ALT_SIZE_640 4 #define SPCA50X_ALT_SIZE_768 5 #define SPCA50X_ALT_SIZE_896 6 #define SPCA50X_ALT_SIZE_1023 7 /* Sequence packet identifier for a dropped packet */ #define SPCA50X_SEQUENCE_DROP 0xFF /* Type bit for 10 byte header snapshot flag */ #define SPCA50X_SNAPBIT 0x40 #define SPCA50X_SNAPCTRL 0x80 /* Offsets into the 10 byte header on the first ISO packet */ #define SPCA50X_OFFSET_SEQUENCE 0 /* Generic frame packet header offsets */ #define SPCA50X_OFFSET_TYPE 1 #define SPCA50X_OFFSET_COMPRESS 2 #define SPCA50X_OFFSET_THRESHOLD 3 #define SPCA50X_OFFSET_QUANT 4 #define SPCA50X_OFFSET_QUANT2 5 #define SPCA50X_OFFSET_FRAMSEQ 6 #define SPCA50X_OFFSET_EDGE_AUDIO 7 #define SPCA50X_OFFSET_GPIO 8 #define SPCA50X_OFFSET_RESERVED 9 #define SPCA50X_OFFSET_DATA 10 /* Bitmask for properties at offsets above */ #define SPCA50X_PROP_COMP_ENABLE(d) ( (d) & 1 ) #define SPCA50X_PROP_SNAP(d) ( (d) & SPCA50X_SNAPBIT ) #define SPCA50X_PROP_SNAP_CTRL(d) ( (d) & SPCA50X_SNAPCTRL ) #define SPCA50X_PROP_COMP_T3A(d) ( ((d) & 0xA ) >> 2) #define SPCA50X_PROP_COMP_T3D(d) ( ((d) & 0x70 ) >> 4) /* USB control */ #define SPCA50X_REG_USB 0x2 #define SPCA50X_USB_CTRL 0x0 #define SPCA50X_CUSB_ENABLE 0x1 #define SPCA50X_CUSB_PREFETCH 0x2 /* Global control register */ #define SPCA50X_REG_GLOBAL 0x3 #define SPCA50X_GLOBAL_MISC0 0x0 // Global control miscellaneous 0 #define SPCA50X_GMISC0_IDSEL 0x1 // Global control device ID select #define SPCA50X_GMISC0_EXTTXEN 0x2 // Global control USB Transceiver select #define SPCA50X_GLOBAL_MISC1 0x1 #define SPCA50X_GMISC1_BLKUSBRESET 0x1 #define SPCA50X_GMISC1_BLKSUSPEND 0x2 #define SPCA50X_GMISC1_DRAMOUTEN 0x10 #define SPCA50X_GMISC1_INTRAMCRTEN 0x20 #define SPCA50X_GLOBAL_MISC2 0x2 #define SPCA50X_GLOBAL_MISC3 0x3 #define SPCA50X_GMISC3_SSC 0x1 #define SPCA50X_GMISC3_SSD 0x2 #define SPCA50X_GMISC3_SAA7113RST 0x20 /* Not sure about this one */ #define SPCA50X_GLOBAL_MISC4 0x4 #define SPCA50X_GMISC4_SSCEN 0x1 #define SPCA50X_GMISC4_SSDEN 0x2 #define SPCA50X_GLOBAL_MISC5 0x5 #define SPCA50X_GMISC5_SSD 0x2 #define SPCA50X_GLOBAL_MISC6 0x6 /* Image format and compression control */ #define SPCA50X_REG_COMPRESS 0x4 #define SPCA50X_COMPRESS_MISC1 0x1 #define SPCA50X_CMISC1_TVFIELDPROCESS 0x40 #define SPCA50X_COMPRESS_ENABLE 0x8 #define SPCA50X_CENABLE_ENABLE 0x1 /* SAA 7113 */ /* TV control register */ #define SPCA50X_REG_TV 0x8 #define SPCA50X_TV_MISC0 0x0 #define SPCA50X_TMISC0_PAL 0x1 #define SPCA50X_TMISC0_SINGLECHANNEL 0x2 #define SPCA50X_TMISC0_EXTFIELD 0x4 #define SPCA50X_TMISC0_INVFIELD 0x8 #define SPCA50X_TMISC0_PIXSEL 0x30 /* Not sure what this does */ #define SPCA50x_TMISC0_ADD128 0x80 /* I2C interface on an SPCA505, SPCA506, SPCA508 */ #define SPCA50X_REG_I2C_CTRL 0x7 #define SPCA50X_I2C_DEVICE 0x4 #define SPCA50X_I2C_SUBADDR 0x1 #define SPCA50X_I2C_VALUE 0x0 #define SPCA50X_I2C_TRIGGER 0x2 #define SPCA50X_I2C_TRIGGER_BIT 0x1 #define SPCA50X_I2C_READ 0x0 #define SPCA50X_I2C_STATUS 0x3 #define SAA7113_REG_STATUS 0x1f #define SAA7113_I2C_BASE_WRITE 0x4A #define SAA7113_I2C_BASE_READ 0x4A /* SPCA50x seems to add the read bit itself */ #define SAA7113_I2C_ALT_BASE_WRITE 0x48 #define SAA7113_I2C_ALT_BASE_READ 0x48 /* SPCA50x seems to add the read bit itself */ #define SAA7113_STATUS_READY(d) (d & 0x1) #define SAA7113_STATUS_COPRO(d) (d & 0x2) #define SAA7113_STATUS_WIPA(d) (d & 0x4) #define SAA7113_STATUS_GLIMB(d) (d & 0x8) #define SAA7113_STATUS_GLIMT(d) (d & 0x10) #define SAA7113_STATUS_FIDT(d) (d & 0x20) #define SAA7113_STATUS_HLVLN(d) (d & 0x40) #define SAA7113_STATUS_INTL(d) (d & 0x80) /* Scratch buffer for 2 lines of YUV data */ #define SCRATCH_BUF_SIZE 3*SPCA50X_MAX_WIDTH /* Brightness autoadjustment parameters*/ #define NSTABLE_MAX 4 #define NUNSTABLE_MAX 600 #define MIN_BRIGHTNESS 10 #if 1 /* moved to pwc-info.h */ #include "pwc-info.h" #else /* Camera type jpeg yuvy yyuv yuyv grey gbrg*/ enum { JPEG = 0, //Jpeg 4.1.1 Sunplus JPGH, //jpeg 4.2.2 Zstar JPGC, //jpeg 4.2.2 Conexant JPGS, //jpeg 4.2.2 Sonix JPGM, //jpeg 4.2.2 Mars-Semi YUVY, YYUV, YUYV, GREY, GBRG, SN9C, // Sonix compressed stream GBGR, S561, // Sunplus Compressed stream PGBRG, // Pixart RGGB bayer }; #endif enum { QCIF = 1, QSIF, QPAL, CIF, SIF, PAL, VGA, CUSTOM, TOTMODE, }; /* available palette */ #define P_RGB16 1 #define P_RGB24 (1 << 1) #define P_RGB32 (1 << 2) #define P_YUV420 (1 << 3) #define P_YUV422 ( 1 << 4) #define P_RAW (1 << 5) #define P_JPEG (1 << 6) /* Format descriptor */ struct mwebcam { int width, height; /* geometry */ __u16 t_palette; __u16 pipe; /* pipe size */ int alt; /* alternate configuration */ int method; /* natural, crop, frame, ... */ int mode; /* camera-specific */ }; struct video_param { int chg_para; #define CHGABRIGHT 1 #define CHGQUALITY 2 #define CHGTINTER 4 __u8 autobright; __u8 quality; __u16 time_interval; }; /* Our private ioctl */ #define SPCAGVIDIOPARAM _IOR('v',BASE_VIDIOCPRIVATE + 1,struct video_param) #define SPCASVIDIOPARAM _IOW('v',BASE_VIDIOCPRIVATE + 2,struct video_param) /* State machine for each frame in the frame buffer during capture */ enum { STATE_SCANNING, /* Scanning for start */ STATE_HEADER, /* Parsing header */ STATE_LINES, /* Parsing lines */ }; /* Buffer states */ enum { BUF_NOT_ALLOCATED, BUF_ALLOCATED, BUF_PEND_DEALLOC, /* spca50x->buf_timer is set */ }; struct usb_device; /* One buffer for the USB ISO transfers */ struct spca50x_sbuf { char *data; struct urb *urb; }; /* States for each frame buffer. */ enum { FRAME_UNUSED, /* Unused (no MCAPTURE) */ FRAME_READY, /* Ready to start grabbing */ FRAME_GRABBING, /* In the process of being grabbed into */ FRAME_DONE, /* Finished grabbing, but not been synced yet */ FRAME_ERROR, /* Something bad happened while processing */ FRAME_ABORTING, /* Aborting everything. Caused by hot unplugging. */ }; /************************ decoding data **************************/ struct pictparam { int change; int force_rgb; int gamma; int OffRed; int OffBlue; int OffGreen; int GRed; int GBlue; int GGreen; }; #define MAXCOMP 4 struct dec_hufftbl; struct enc_hufftbl; union hufftblp { struct dec_hufftbl *dhuff; struct enc_hufftbl *ehuff; }; struct scan { int dc; /* old dc value */ union hufftblp hudc; /* pointer to huffman table dc */ union hufftblp huac; /* pointer to huffman table ac */ int next; /* when to switch to next scan */ int cid; /* component id */ int hv; /* horiz/vert, copied from comp */ int tq; /* quant tbl, copied from comp */ }; /*********************************/ #define DECBITS 10 /* seems to be the optimum */ struct dec_hufftbl { int maxcode[17]; int valptr[16]; unsigned char vals[256]; unsigned int llvals[1 << DECBITS]; }; /*********************************/ struct in { unsigned char *p; unsigned int bits; int omitescape; int left; int marker; }; struct jpginfo { int nc; /* number of components */ int ns; /* number of scans */ int dri; /* restart interval */ int nm; /* mcus til next marker */ int rm; /* next restart marker */ }; struct comp { int cid; int hv; int tq; }; /* Sonix decompressor struct B.S.(2004) */ struct code_table_t { int is_abs; int len; int val; }; struct dec_data { struct in in; struct jpginfo info; struct comp comps[MAXCOMP]; struct scan dscans[MAXCOMP]; unsigned char quant[3][64]; int dquant[3][64]; struct code_table_t table[256]; unsigned char Red[256]; unsigned char Green[256]; unsigned char Blue[256]; }; /*************************End decoding data ********************************/ struct spca50x_frame { unsigned char *data; /* Frame buffer */ unsigned char *tmpbuffer; /* temporary buffer spca50x->tmpbuffer need for decoding */ struct dec_data *decoder; /* Memory allocation for the jpeg decoders */ int dcts[6 * 64 + 16]; int out[6 * 64]; int max[6]; /*******************************************/ int seq; /* Frame sequence number */ int depth; /* Bytes per pixel */ int width; /* Width application is expecting */ int height; /* Height */ int hdrwidth; /* Width the frame actually is */ int hdrheight; /* Height */ int method; /* The decoding method for that frame 0 nothing 1 crop 2 div 4 mult */ int cropx1; /* value to be send with the frame for decoding feature */ int cropx2; int cropy1; int cropy2; int x; int y; unsigned int format; /* Format asked by apps for this frame */ int cameratype; /* native in frame format */ struct pictparam pictsetting; volatile int grabstate; /* State of grabbing */ int scanstate; /* State of scanning */ long scanlength; /* uncompressed, raw data length of frame */ int totlength; /* length of the current reading byte in the Iso stream */ #if !defined(__FreeBSD__) wait_queue_head_t wq; /* Processes waiting */ #endif int snapshot; /* True if frame was a snapshot */ int last_packet; /* sequence number for last packet */ unsigned char *highwater; /* used for debugging */ }; struct usb_spca50x { struct usb_device *dev; /* Device structure */ int desc; /* enum camera name */ int vendor; int product; void *pwc_softc; #define PWC_SC(x) ((struct pwc_softc *)(x)->pwc_softc) int vendpoint; /* endpoint id (not offset!) for ISOC stream */ #if !defined(__FreeBSD__) struct video_device *vdev; struct tasklet_struct spca5xx_tasklet; /* use a tasklet per device */ struct spca50x_frame frame[SPCA50X_NUMFRAMES]; #endif struct dec_data maindecode; unsigned long last_times; //timestamp unsigned int dtimes; //nexttimes to acquire unsigned char iface; /* interface in use */ int alt; /* current alternate setting */ int customid; /* product id get by probe */ int ccd; /* If true, using the CCD otherwise the external input */ int chip_revision; /* set when probe the camera spca561 zc0301p for vm303 */ struct mwebcam mode_cam[TOTMODE]; /* all available mode registers by probe */ // int bridge; /* Type of bridge (BRIDGE_SPCA505 or BRIDGE_SPCA506) */ // int sensor; /* Type of image sensor chip */ int packet_size; /* Frame size per isoc desc */ int header_len; /* Determined by sensor type */ int maxwidth; int maxheight; int minwidth; int minheight; /* What we think the hardware is currently set to */ int brightness; int colour; int contrast; int hue; int whiteness; int exposure; // used by spca561 int autoexpo; int qindex; int width; /* use here for the init of each frame */ int height; int hdrwidth; int hdrheight; unsigned int format; int method; /* method ask for output pict */ int mode; /* requested frame size */ int pipe_size; // requested pipe size set according to mode __u16 norme; /* norme in use Pal Ntsc Secam */ __u16 channel; /* input composite video1 or svideo */ int cameratype; /* native in frame format */ struct pictparam pictsetting; /* Statistics variables */ int avg_lum; //The average luminance (if available from theframe header) int avg_bg, avg_rg; //The average B-G and R-G for white balancing int user; /* user count for exclusive use */ int present; /* driver loaded */ int streaming; /* Are we streaming Isochronous? */ int grabbing; /* Are we grabbing? */ int packet; int compress; /* Should the next frame be compressed? */ char *fbuf; /* Videodev buffer area */ int curframe; /* Current receiving frame buffer */ int cursbuf; /* Current receiving sbuf */ /* Temporary jpeg decoder workspace */ unsigned char *tmpBuffer; /* Framebuffer/sbuf management */ int buf_state; #if !defined(__FreeBSD__) struct spca50x_sbuf sbuf[SPCA50X_NUMSBUF]; struct semaphore lock; spinlock_t v4l_lock; /* lock to protect shared data between isoc and process context */ struct semaphore buf_lock; wait_queue_head_t wq; /* Processes waiting */ /* proc interface */ struct semaphore param_lock; /* params lock for this camera */ struct proc_dir_entry *proc_entry; /* /proc/spca50x/videoX */ struct proc_dir_entry *ctl_proc_entry; /* /proc/spca50x/controlX */ #endif /* !__FreeBSD__ */ int lastFrameRead; uint i2c_ctrl_reg; // Camera I2C control register uint i2c_base; // Camera I2C address base char i2c_trigger_on_write; //do trigger bit on write __u8 force_rgb; //Read RGB instead of BGR __u8 min_bpp; //The minimal color depth that may be set __u8 lum_level; //Luminance level for brightness autoadjustment #ifdef SPCA50X_ENABLE_EXPERIMENTAL uint nstable; // the stable condition counter uint nunstable; // the unstable position counter __u8 a_red, a_green, a_blue; //initial values of color corrections params. #endif /* SPCA50X_ENABLE_EXPERIMENTAL */ }; struct palette_list { int num; const char *name; }; struct mode_list { int width; int height; int color; /* 0=grayscale, 1=color */ u8 pxcnt; /* pixel counter */ u8 lncnt; /* line counter */ u8 pxdv; /* pixel divisor */ u8 lndv; /* line divisor */ u8 m420; u8 common_A; u8 common_L; }; #endif /* __KERNEL__ */ #endif /* SPCA50X_H */ pwcbsd/spca5xx-20060402/drivers/usb/spcadecoder.c000755 000423 000000 00000312150 10553461761 021770 0ustar00luigiwheel000000 000000 /******************************************************************************* # spcadecoder: Generic decoder for various input stream yyuv # # yuyv yuvy jpeg411 jpeg422 bayer rggb with gamma correct # # and various output palette rgb16 rgb24 rgb32 yuv420p # # various output size with crop feature # # Copyright (C) 2003 2004 2005 Michel Xhaard # # mxhaard@magic.fr # # Sonix Decompressor by Bertrik.Sikken. (C) 2004 # # Pixart Decompressor by Bertrik.Sikken. Thomas Kaiser (C) 2005 # # Spca561decoder (C) 2005 Andrzej Szombierski [qq@kuku.eu.org] # # This program is free software; you can redistribute it and/or modify # # it under the terms of the GNU General Public License as published by # # the Free Software Foundation; either version 2 of the License, or # # (at your option) any later version. # # # # This program is distributed in the hope that it will be useful, # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # ********************************************************************************/ #if !defined(__FreeBSD__) #ifndef __KERNEL__ #include #include #include #else /* __KERNEL__ */ #include #endif /* __KERNEL__ */ #endif /* !__FreeBSD__ */ #include "pwc.h" #include "spcadecoder.h" #include "spcagamma.h" #define ISHIFT 11 #define IFIX(a) ((long)((a) * (1 << ISHIFT) + .5)) #define IMULT(a, b) (((a) * (b)) >> ISHIFT) #define ITOINT(a) ((a) >> ISHIFT) /* special markers */ #define M_BADHUFF -1 #define ERR_NO_SOI 1 #define ERR_NOT_8BIT 2 #define ERR_HEIGHT_MISMATCH 3 #define ERR_WIDTH_MISMATCH 4 #define ERR_BAD_WIDTH_OR_HEIGHT 5 #define ERR_TOO_MANY_COMPPS 6 #define ERR_ILLEGAL_HV 7 #define ERR_QUANT_TABLE_SELECTOR 8 #define ERR_NOT_YCBCR_221111 9 #define ERR_UNKNOWN_CID_IN_SCAN 10 #define ERR_NOT_SEQUENTIAL_DCT 11 #define ERR_WRONG_MARKER 12 #define ERR_NO_EOI 13 #define ERR_BAD_TABLES 14 #define ERR_DEPTH_MISMATCH 15 #define ERR_CORRUPTFRAME 16 #define JPEGHEADER_LENGTH 589 const unsigned char JPEGHeader[JPEGHEADER_LENGTH] = { 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x06, 0x04, 0x05, 0x06, 0x05, 0x04, 0x06, 0x06, 0x05, 0x06, 0x07, 0x07, 0x06, 0x08, 0x0a, 0x10, 0x0a, 0x0a, 0x09, 0x09, 0x0a, 0x14, 0x0e, 0x0f, 0x0c, 0x10, 0x17, 0x14, 0x18, 0x18, 0x17, 0x14, 0x16, 0x16, 0x1a, 0x1d, 0x25, 0x1f, 0x1a, 0x1b, 0x23, 0x1c, 0x16, 0x16, 0x20, 0x2c, 0x20, 0x23, 0x26, 0x27, 0x29, 0x2a, 0x29, 0x19, 0x1f, 0x2d, 0x30, 0x2d, 0x28, 0x30, 0x25, 0x28, 0x29, 0x28, 0x01, 0x07, 0x07, 0x07, 0x0a, 0x08, 0x0a, 0x13, 0x0a, 0x0a, 0x13, 0x28, 0x1a, 0x16, 0x1a, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0xff, 0xc4, 0x01, 0xa2, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 0xc0, 0x00, 0x11, 0x08, 0x01, 0xe0, 0x02, 0x80, 0x03, 0x01, 0x21, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00 }; #define GSMART_JPG_HUFFMAN_TABLE_LENGTH 0x1A0 const unsigned char GsmartJPEGHuffmanTable[GSMART_JPG_HUFFMAN_TABLE_LENGTH] = { 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7D, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33, 0x52, 0xF0, 0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA }; const unsigned char GsmartJPEGScanTable[6] = { 0x01, 0x00, 0x02, 0x11, 0x03, 0x11 }; const unsigned char GsmartQTable[][64] = { //index0,Q40 { 20, 14, 15, 18, 15, 13, 20, 18, 16, 18, 23, 21, 20, 24, 30, 50, 33, 30, 28, 28, 30, 61, 44, 46, 36, 50, 73, 64, 76, 75, 71, 64, 70, 69, 80, 90, 115, 98, 80, 85, 109, 86, 69, 70, 100, 136, 101, 109, 119, 123, 129, 130, 129, 78, 96, 141, 151, 140, 125, 150, 115, 126, 129, 124}, { 21, 23, 23, 30, 26, 30, 59, 33, 33, 59, 124, 83, 70, 83, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124}, //index1,Q50 { 16, 11, 12, 14, 12, 10, 16, 14, 13, 14, 18, 17, 16, 19, 24, 40, 26, 24, 22, 22, 24, 49, 35, 37, 29, 40, 58, 51, 61, 60, 57, 51, 56, 55, 64, 72, 92, 78, 64, 68, 87, 69, 55, 56, 80, 109, 81, 87, 95, 98, 103, 104, 103, 62, 77, 113, 121, 112, 100, 120, 92, 101, 103, 99}, { 17, 18, 18, 24, 21, 24, 47, 26, 26, 47, 99, 66, 56, 66, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}, //index2,Q60 { 13, 9, 10, 11, 10, 8, 13, 11, 10, 11, 14, 14, 13, 15, 19, 32, 21, 19, 18, 18, 19, 39, 28, 30, 23, 32, 46, 41, 49, 48, 46, 41, 45, 44, 51, 58, 74, 62, 51, 54, 70, 55, 44, 45, 64, 87, 65, 70, 76, 78, 82, 83, 82, 50, 62, 90, 97, 90, 80, 96, 74, 81, 82, 79}, { 14, 14, 14, 19, 17, 19, 38, 21, 21, 38, 79, 53, 45, 53, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79}, //index3,Q70 { 10, 7, 7, 8, 7, 6, 10, 8, 8, 8, 11, 10, 10, 11, 14, 24, 16, 14, 13, 13, 14, 29, 21, 22, 17, 24, 35, 31, 37, 36, 34, 31, 34, 33, 38, 43, 55, 47, 38, 41, 52, 41, 33, 34, 48, 65, 49, 52, 57, 59, 62, 62, 62, 37, 46, 68, 73, 67, 60, 72, 55, 61, 62, 59}, { 10, 11, 11, 14, 13, 14, 28, 16, 16, 28, 59, 40, 34, 40, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59}, //index4,Q80 { 6, 4, 5, 6, 5, 4, 6, 6, 5, 6, 7, 7, 6, 8, 10, 16, 10, 10, 9, 9, 10, 20, 14, 15, 12, 16, 23, 20, 24, 24, 23, 20, 22, 22, 26, 29, 37, 31, 26, 27, 35, 28, 22, 22, 32, 44, 32, 35, 38, 39, 41, 42, 41, 25, 31, 45, 48, 45, 40, 48, 37, 40, 41, 40}, { 7, 7, 7, 10, 8, 10, 19, 10, 10, 19, 40, 26, 22, 26, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40}, //index5Q85 { 5, 3, 4, 4, 4, 3, 5, 4, 4, 4, 5, 5, 5, 6, 7, 12, 8, 7, 7, 7, 7, 15, 11, 11, 9, 12, 17, 15, 18, 18, 17, 15, 17, 17, 19, 22, 28, 23, 19, 20, 26, 21, 17, 17, 24, 33, 24, 26, 29, 29, 31, 31, 31, 19, 23, 34, 36, 34, 30, 36, 28, 30, 31, 30}, { 5, 5, 5, 7, 6, 7, 14, 8, 8, 14, 30, 20, 17, 20, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, /* Qindex= 86 */ { 0x04, 0x03, 0x03, 0x04, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x04, 0x05, 0x07, 0x0B, 0x07, 0x07, 0x06, 0x06, 0x07, 0x0E, 0x0A, 0x0A, 0x08, 0x0B, 0x10, 0x0E, 0x11, 0x11, 0x10, 0x0E, 0x10, 0x0F, 0x12, 0x14, 0x1A, 0x16, 0x12, 0x13, 0x18, 0x13, 0x0F, 0x10, 0x16, 0x1F, 0x17, 0x18, 0x1B, 0x1B, 0x1D, 0x1D, 0x1D, 0x11, 0x16, 0x20, 0x22, 0x1F, 0x1C, 0x22, 0x1A, 0x1C, 0x1D, 0x1C, }, {0x05, 0x05, 0x05, 0x07, 0x06, 0x07, 0x0D, 0x07, 0x07, 0x0D, 0x1C, 0x12, 0x10, 0x12, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, }, /* Qindex= 88 */ { 0x04, 0x03, 0x03, 0x03, 0x03, 0x02, 0x04, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x05, 0x06, 0x0A, 0x06, 0x06, 0x05, 0x05, 0x06, 0x0C, 0x08, 0x09, 0x07, 0x0A, 0x0E, 0x0C, 0x0F, 0x0E, 0x0E, 0x0C, 0x0D, 0x0D, 0x0F, 0x11, 0x16, 0x13, 0x0F, 0x10, 0x15, 0x11, 0x0D, 0x0D, 0x13, 0x1A, 0x13, 0x15, 0x17, 0x18, 0x19, 0x19, 0x19, 0x0F, 0x12, 0x1B, 0x1D, 0x1B, 0x18, 0x1D, 0x16, 0x18, 0x19, 0x18, }, {0x04, 0x04, 0x04, 0x06, 0x05, 0x06, 0x0B, 0x06, 0x06, 0x0B, 0x18, 0x10, 0x0D, 0x10, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, } }; static int jpeg_decode411(struct spca50x_frame *myframe, int force_rgb); static int jpeg_decode422(struct spca50x_frame *myframe, int force_rgb); static int yuv_decode(struct spca50x_frame *myframe, int force_rgb); static int bayer_decode(struct spca50x_frame *myframe, int force_rgb); static int make_jpeg(struct spca50x_frame *myframe); static int make_jpeg_conexant(struct spca50x_frame *myframe); #define CLIP(color) (unsigned char)(((color)>0xFF)?0xff:(((color)<0)?0:(color))) /****************************************************************/ /************** Sonix huffman decoder ****************/ /****************************************************************/ static inline unsigned char getByte(unsigned char *inp, unsigned int bitpos) { unsigned char *addr; addr = inp + (bitpos >> 3); return (addr[0] << (bitpos & 7)) | (addr[1] >> (8 - (bitpos & 7))); } void init_sonix_decoder(struct usb_spca50x *spca50x) { int i; int is_abs, val, len; struct code_table_t *table = spca50x->maindecode.table; for (i = 0; i < 256; i++) { is_abs = 0; val = 0; len = 0; if ((i & 0x80) == 0) { /* code 0 */ val = 0; len = 1; } else if ((i & 0xE0) == 0x80) { /* code 100 */ val = +4; len = 3; } else if ((i & 0xE0) == 0xA0) { /* code 101 */ val = -4; len = 3; } else if ((i & 0xF0) == 0xD0) { /* code 1101 */ val = +11; len = 4; } else if ((i & 0xF0) == 0xF0) { /* code 1111 */ val = -11; len = 4; } else if ((i & 0xF8) == 0xC8) { /* code 11001 */ val = +20; len = 5; } else if ((i & 0xFC) == 0xC0) { /* code 110000 */ val = -20; len = 6; } else if ((i & 0xFC) == 0xC4) { /* code 110001xx: unknown */ val = -1; len = 8; } else if ((i & 0xF0) == 0xE0) { /* code 1110xxxx */ is_abs = 1; val = (i & 0x0F) << 4; len = 8; } table[i].is_abs = is_abs; table[i].val = val; table[i].len = len; } } static void sonix_decompress(struct spca50x_frame *myframe) { int width = myframe->hdrwidth; int height = myframe->hdrheight; unsigned char *inp = myframe->data; unsigned char *outp = myframe->tmpbuffer; struct code_table_t *table = myframe->decoder->table; int row, col; int val; int bitpos; unsigned char code; bitpos = 0; for (row = 0; row < height; row++) { col = 0; /* first two pixels in first two rows are stored as raw 8-bit */ if (row < 2) { code = getByte(inp, bitpos); bitpos += 8; *outp++ = code; code = getByte(inp, bitpos); bitpos += 8; *outp++ = code; col += 2; } while (col < width) { /* get bitcode from bitstream */ code = getByte(inp, bitpos); /* update bit position */ bitpos += table[code].len; /* calculate pixel value */ val = table[code].val; /* unknowcode output nothing BS update 23:10:2005 */ if (val == -1) continue; if (!table[code].is_abs) { /* value is relative to top and left pixel */ if (col < 2) { /* left column: relative to top pixel */ val += outp[-2 * width]; } else if (row < 2) { /* top row: relative to left pixel */ val += outp[-2]; } else { /* main area: average of left pixel and top pixel */ val += (outp[-2] + outp[-2 * width]) / 2; //val += (outp[-2] + outp[-2*width] - outp[-2*width -2]); } } /* store pixel */ *outp++ = CLIP(val); col++; } } } #if 1 /* XXX note, imported in pwc-spca.c */ void init_pixart_decoder(struct usb_spca50x *spca50x) { int i; int is_abs, val, len; struct code_table_t *table = spca50x->maindecode.table; for (i = 0; i < 256; i++) { is_abs = 0; val = 0; len = 0; if ((i & 0xC0) == 0) { /* code 00 */ val = 0; len = 2; } else if ((i & 0xC0) == 0x40) { /* code 01 */ val = -5; len = 2; } else if ((i & 0xC0) == 0x80) { /* code 10 */ val = +5; len = 2; } else if ((i & 0xF0) == 0xC0) { /* code 1100 */ val = -10; len = 4; } else if ((i & 0xF0) == 0xD0) { /* code 1101 */ val = +10; len = 4; } else if ((i & 0xF8) == 0xE0) { /* code 11100 */ val = -15; len = 5; } else if ((i & 0xF8) == 0xE8) { /* code 11101 */ val = +15; len = 5; } else if ((i & 0xFC) == 0xF0) { /* code 111100 */ val = -20; len = 6; } else if ((i & 0xFC) == 0xF4) { /* code 111101 */ val = +20; len = 6; } else if ((i & 0xF8) == 0xF8) { /* code 11111xxxxxx */ is_abs = 1; val = 0; len = 5; } table[i].is_abs = is_abs; table[i].val = val; table[i].len = len; } } static int pac_decompress_row(struct code_table_t *table, unsigned char *inp, unsigned char *outp, int width) { int col; int val; int bitpos; unsigned char code; /* first two pixels are stored as raw 8-bit */ *outp++ = inp[2]; *outp++ = inp[3]; bitpos = 32; /* main decoding loop */ for (col = 2; col < width; col++) { /* get bitcode */ code = getByte(inp, bitpos); bitpos += table[code].len; /* calculate pixel value */ if (table[code].is_abs) { /* absolute value: get 6 more bits */ code = getByte(inp, bitpos); bitpos += 6; *outp++ = code & 0xFC; } else { /* relative to left pixel */ val = outp[-2] + table[code].val; *outp++ = CLIP(val); } } /* return line length, rounded up to next 16-bit word */ return 2 * ((bitpos + 15) / 16); } static inline unsigned short getShort(unsigned char *pt) { return ((pt[0] << 8) | pt[1]); } static int pixart_decompress(struct spca50x_frame *myframe) { /* we should received a whole frame with header and EOL marker in myframe->data and return a GBRG pattern in frame->tmpbuffer remove the header then copy line by line EOL is set with 0x0f 0xf0 marker or 0x1e 0xe1 for compressed line*/ int width = myframe->hdrwidth; int height = myframe->hdrheight; unsigned char *outp = myframe->tmpbuffer; unsigned char *inp = myframe->data; struct code_table_t *table = myframe->decoder->table; unsigned short word; int row; /* skip header */ inp += 16; /* and ask to go at pixel +1 ?? */ outp++; /* iterate over all rows */ for (row = 0; row < height; row++) { word = getShort(inp); switch (word) { case 0x0FF0: memcpy(outp, inp + 2, width); inp += (2 + width); break; case 0x1EE1: inp += pac_decompress_row(table, inp, outp, width); break; default: return -1; } outp += width; } return 0; } #endif /* imported in pwc-spca.c */ static void tv8532_preprocess(struct spca50x_frame *myframe) { /* we should received a whole frame with header and EOL marker in myframe->data and return a GBRG pattern in frame->tmpbuffer sequence 2bytes header the Alternate pixels bayer GB 4 bytes Alternate pixels bayer RG 4 bytes EOL */ int width = myframe->hdrwidth; int height = myframe->hdrheight; int src = 0; unsigned char *dst = myframe->tmpbuffer; unsigned char *data = myframe->data; int i; int seq1, seq2; /* precompute where is the good bayer line */ if ((((data[src + 3] + data[src + width + 7]) >> 1) + (data[src + 4] >> 2) + (data[src + width + 6] >> 1)) >= (((data[src + 2] + data[src + width + 6]) >> 1) + (data[src + 3] >> 2) + (data[src + width + 5] >> 1))) { seq1 = 3; seq2 = 4; } else { seq1 = 2; seq2 = 5; } for (i = 0; i < height / 2; i++) { src += seq1; memcpy(dst, &myframe->data[src], width); src += (width + 3); dst += width; memcpy(dst, &myframe->data[src], width); src += (width + seq2); dst += width; } } /* # Decoder for compressed spca561 images # # It was developed for "Labtec WebCam Elch 2(SPCA561A)" (046d:0929) # # but it might work with other spca561 cameras # */ static unsigned int bit_bucket; static unsigned char *input_ptr; static inline void refill(int *bitfill) { if (*bitfill < 8) { bit_bucket = (bit_bucket << 8) | *(input_ptr++); *bitfill += 8; } } static inline int nbits(int *bitfill, int n) { bit_bucket = (bit_bucket << 8) | *(input_ptr++); *bitfill -= n; return (bit_bucket >> (*bitfill & 0xff)) & ((1 << n) - 1); } static inline int _nbits(int *bitfill, int n) { *bitfill -= n; return (bit_bucket >> (*bitfill & 0xff)) & ((1 << n) - 1); } static int fun_A(int *bitfill) { int ret; static int tab[] = { 12, 13, 14, 15, 16, 17, 18, 19, -12, -13, -14, -15, -16, -17, -18, -19, -19 }; ret = tab[nbits(bitfill, 4)]; refill(bitfill); return ret; } static int fun_B(int *bitfill) { static int tab1[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 }; static int tab[] = { 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19 }; unsigned int tmp; tmp = nbits(bitfill, 7) - 68; refill(bitfill); if (tmp > 47) return 0xff; return tab[tab1[tmp]]; } static int fun_C(int *bitfill, int gkw) { static int tab1[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22 }; static int tab[] = { 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19 }; unsigned int tmp; if (gkw == 0xfe) { if (nbits(bitfill, 1) == 0) return 7; else return -8; } if (gkw != 0xff) return 0xff; tmp = nbits(bitfill, 7) - 72; if (tmp > 43) return 0xff; refill(bitfill); return tab[tab1[tmp]]; } static int fun_D(int *bitfill, int gkw) { if (gkw == 0xfd) { if (nbits(bitfill, 1) == 0) return 12; else return -13; } if (gkw == 0xfc) { if (nbits(bitfill, 1) == 0) return 13; else return -14; } if (gkw == 0xfe) { switch (nbits(bitfill, 2)) { case 0: return 14; case 1: return -15; case 2: return 15; case 3: return -16; } } if (gkw == 0xff) { switch (nbits(bitfill, 3)) { case 4: return 16; case 5: return -17; case 6: return 17; case 7: return -18; case 2: return _nbits(bitfill, 1) ? 0xed : 0x12; case 3: *bitfill--; return 18; } return 0xff; } return gkw; } static int fun_E(int cur_byte, int *bitfill) { static int tab0[] = { 0, -1, 1, -2, 2, -3, 3, -4 }; static int tab1[] = { 4, -5, 5, -6, 6, -7, 7, -8 }; static int tab2[] = { 8, -9, 9, -10, 10, -11, 11, -12 }; static int tab3[] = { 12, -13, 13, -14, 14, -15, 15, -16 }; static int tab4[] = { 16, -17, 17, -18, 18, -19, 19, -19 }; if ((cur_byte & 0xf0) >= 0x80) { *bitfill -= 4; return tab0[(cur_byte >> 4) & 7]; } else if ((cur_byte & 0xc0) == 0x40) { *bitfill -= 5; return tab1[(cur_byte >> 3) & 7]; } else if ((cur_byte & 0xe0) == 0x20) { *bitfill -= 6; return tab2[(cur_byte >> 2) & 7]; } else if ((cur_byte & 0xf0) == 0x10) { *bitfill -= 7; return tab3[(cur_byte >> 1) & 7]; } else if ((cur_byte & 0xf8) == 8) { *bitfill -= 8; return tab4[cur_byte & 7]; } return 0xff; } static int fun_F(int cur_byte, int *bitfill) { *bitfill -= 5; switch (cur_byte & 0xf8) { case 0x80: return 0; case 0x88: return -1; case 0x90: return 1; case 0x98: return -2; case 0xa0: return 2; case 0xa8: return -3; case 0xb0: return 3; case 0xb8: return -4; case 0xc0: return 4; case 0xc8: return -5; case 0xd0: return 5; case 0xd8: return -6; case 0xe0: return 6; case 0xe8: return -7; case 0xf0: return 7; case 0xf8: return -8; } *bitfill -= 1; switch (cur_byte & 0xfc) { case 0x40: return 8; case 0x44: return -9; case 0x48: return 9; case 0x4c: return -10; case 0x50: return 10; case 0x54: return -11; case 0x58: return 11; case 0x5c: return -12; case 0x60: return 12; case 0x64: return -13; case 0x68: return 13; case 0x6c: return -14; case 0x70: return 14; case 0x74: return -15; case 0x78: return 15; case 0x7c: return -16; } *bitfill -= 1; switch (cur_byte & 0xfe) { case 0x20: return 16; case 0x22: return -17; case 0x24: return 17; case 0x26: return -18; case 0x28: return 18; case 0x2a: return -19; case 0x2c: return 19; } *bitfill += 7; return 0xff; } static int internal_spca561_decode(int width, int height, unsigned char *inbuf, unsigned char *outbuf) // {{{ { // buffers static int accum[8 * 8 * 8]; static int i_hits[8 * 8 * 8]; const static int nbits_A[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, }; const static int tab_A[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, -11, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 255, 254, -4, -4, -5, -5, -6, -6, -7, -7, -8, -8, -9, -9, -10, -10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, -2, -2, -2, -2, -2, -2, -2, -2, -3, -3, -3, -3, -3, -3, -3, -3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; const static int nbits_B[] = { 0, 8, 7, 7, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, }; const static int tab_B[] = { 0xff, -4, 3, 3, -3, -3, -3, -3, 2, 2, 2, 2, 2, 2, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; const static int nbits_C[] = { 0, 0, 8, 8, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, }; const static int tab_C[] = { 0xff, 0xfe, 6, -7, 5, 5, -6, -6, 4, 4, 4, 4, -5, -5, -5, -5, 3, 3, 3, 3, 3, 3, 3, 3, -4, -4, -4, -4, -4, -4, -4, -4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, }; const static int nbits_D[] = { 0, 0, 0, 0, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }; const static int tab_D[] = { 0xff, 0xfe, 0xfd, 0xfc, 10, -11, 11, -12, 8, 8, -9, -9, 9, 9, -10, -10, 6, 6, 6, 6, -7, -7, -7, -7, 7, 7, 7, 7, -8, -8, -8, -8, 4, 4, 4, 4, 4, 4, 4, 4, -5, -5, -5, -5, -5, -5, -5, -5, 5, 5, 5, 5, 5, 5, 5, 5, -6, -6, -6, -6, -6, -6, -6, -6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }; // a_curve[19 + i] = ... [-19..19] => [-160..160] const static int a_curve[] = { -160, -144, -128, -112, -98, -88, -80, -72, -64, -56, -48, -40, -32, -24, -18, -12, -8, -5, -2, 0, 2, 5, 8, 12, 18, 24, 32, 40, 48, 56, 64, 72, 80, 88, 98, 112, 128, 144, 160 }; // clamp0_255[256 + i] = min(max(i,255),0) const static unsigned char clamp0_255[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }; // abs_clamp15[19 + i] = min(abs(i), 15) const static int abs_clamp15[] = { 15, 15, 15, 15, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 15, 15, 15 }; // diff_encoding[256 + i] = ... const static int diff_encoding[] = { 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 3, 3, 3, 3, 1, 1, 0, 2, 2, 4, 4, 4, 4, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }; int block; int bitfill = 0; int xwidth = width + 6; int off_up_right = 2 - 2 * xwidth; int off_up_left = -2 - 2 * xwidth; int pixel_U = 0, saved_pixel_UR = 0; int pixel_x = 0, pixel_y = 2; unsigned char *output_ptr = outbuf; memset(i_hits, 0, sizeof(i_hits)); memset(accum, 0, sizeof(accum)); memcpy(outbuf + xwidth * 2 + 3, inbuf + 0x14, width); memcpy(outbuf + xwidth * 3 + 3, inbuf + 0x14 + width, width); input_ptr = inbuf + 0x14 + width * 2; output_ptr = outbuf + (xwidth) * 4 + 3; bit_bucket = 0; for (block = 0; block < ((height - 2) * width) / 32; ++block) { int b_it, var_7 = 0; int cur_byte; refill(&bitfill); cur_byte = (bit_bucket >> (bitfill & 7)) & 0xff; if ((cur_byte & 0x80) == 0) { var_7 = 0; bitfill--; } else if ((cur_byte & 0xC0) == 0x80) { var_7 = 1; bitfill -= 2; } else if ((cur_byte & 0xc0) == 0xc0) { var_7 = 2; bitfill -= 2; } for (b_it = 0; b_it < 32; b_it++) { int index; int pixel_L, pixel_UR, pixel_UL; int multiplier; int dL, dC, dR; int gkw; // God knows what refill(&bitfill); cur_byte = bit_bucket >> (bitfill & 7) & 0xff; pixel_L = output_ptr[-2]; pixel_UR = output_ptr[off_up_right]; pixel_UL = output_ptr[off_up_left]; dL = diff_encoding[0x100 + pixel_UL - pixel_L]; dC = diff_encoding[0x100 + pixel_U - pixel_UL]; dR = diff_encoding[0x100 + pixel_UR - pixel_U]; if (pixel_x < 2) { pixel_L = pixel_UL = pixel_U = output_ptr[-xwidth * 2]; pixel_UR = output_ptr[off_up_right]; dL = dC = 0; dR = diff_encoding[0x100 + pixel_UR - pixel_U]; } else if (pixel_x > width - 3) dR = 0; multiplier = 4; index = dR + dC * 8 + dL * 64; if (pixel_L + pixel_U * 2 <= 144 && (pixel_y & 1) == 0 && (b_it & 3) == 0 && (dR < 5) && (dC < 5) && (dL < 5)) { multiplier = 1; } else if (pixel_L <= 48 && dL <= 4 && dC <= 4 && dL >= 1 && dC >= 1) { multiplier = 2; } else if (var_7 == 1) { multiplier = 2; } else if (dC + dL >= 11 || var_7 == 2) { multiplier = 8; } if (i_hits[index] < 7) { bitfill -= nbits_A[cur_byte]; gkw = tab_A[cur_byte]; if (gkw == 0xfe) gkw = fun_A(&bitfill); } else if (i_hits[index] >= accum[index]) { bitfill -= nbits_B[cur_byte]; gkw = tab_B[cur_byte]; if (cur_byte == 0) gkw = fun_B(&bitfill); } else if (i_hits[index] * 2 >= accum[index]) { bitfill -= nbits_C[cur_byte]; gkw = tab_C[cur_byte]; if (cur_byte < 2) gkw = fun_C(&bitfill, gkw); } else if (i_hits[index] * 4 >= accum[index]) { bitfill -= nbits_D[cur_byte]; gkw = tab_D[cur_byte]; if (cur_byte < 4) gkw = fun_D(&bitfill, gkw); } else if (i_hits[index] * 8 >= accum[index]) { gkw = fun_E(cur_byte, &bitfill); } else { gkw = fun_F(cur_byte, &bitfill); } if (gkw == 0xff) return -3; { int tmp1, tmp2; tmp1 = (pixel_U + pixel_L) * 3 - pixel_UL * 2; tmp1 += (tmp1 < 0) ? 3 : 0; tmp2 = a_curve[19 + gkw] * multiplier; tmp2 += (tmp2 < 0) ? 1 : 0; *(output_ptr++) = clamp0_255[0x100 + (tmp1 >> 2) - (tmp2 >> 1)]; } pixel_U = saved_pixel_UR; saved_pixel_UR = pixel_UR; if (++pixel_x == width) { output_ptr += 6; pixel_x = 0; pixel_y++; } accum[index] += abs_clamp15[19 + gkw]; if (i_hits[index]++ == 15) { i_hits[index] = 8; accum[index] /= 2; } } } return 0; } static void decode_spca561(unsigned char *inbuf, char *outbuf, int width, int height) { int i; static char tmpbuf[650 * 490]; if (internal_spca561_decode(width, height, inbuf, tmpbuf) == 0) { for (i = 0; i < height; i++) memcpy(outbuf + i * width, tmpbuf + (i + 2) * (width + 6) + 3, width); } } /****************************************************************/ /************** huffman decoder ***************/ /****************************************************************/ /*need to be on init jpeg */ static struct comp comp_template[MAXCOMP] = { {0x01, 0x22, 0x00}, {0x02, 0x11, 0x01}, {0x03, 0x11, 0x01}, {0x00, 0x00, 0x00} }; /* deprecated set by webcam now in spca50x */ //static struct scan dscans[MAXCOMP]; //static unsigned char quant[3][64]; //static struct in in; //int dquant[3][64]; //static struct jpginfo info; /* table de Huffman global for all */ static struct dec_hufftbl dhuff[4]; #define dec_huffdc (dhuff + 0) #define dec_huffac (dhuff + 2) #define M_RST0 0xd0 static int fillbits(struct in *, int, unsigned int); static int dec_rec2(struct in *, struct dec_hufftbl *, int *, int, int); static int fillbits(struct in *in, int le, unsigned int bi) { int b; int m; if (in->marker) { if (le <= 16) in->bits = bi << 16, le += 16; return le; } while (le <= 24) { b = *in->p++; if (in->omitescape) { if (b == 0xff && (m = *in->p++) != 0) { in->marker = m; if (le <= 16) bi = bi << 16, le += 16; break; } } bi = bi << 8 | b; le += 8; } in->bits = bi; /* tmp... 2 return values needed */ return le; } #define LEBI_GET(in) (le = in->left, bi = in->bits) #define LEBI_PUT(in) (in->left = le, in->bits = bi) #define GETBITS(in, n) ( \ (le < (n) ? le = fillbits(in, le, bi), bi = in->bits : 0), \ (le -= (n)), \ bi >> le & ((1 << (n)) - 1) \ ) #define UNGETBITS(in, n) ( \ le += (n) \ ) static void dec_initscans(struct dec_data *decode) { struct jpginfo *info = &decode->info; struct scan *dscans = decode->dscans; int i; info->ns = 3; // HARDCODED here info->nm = info->dri + 1; // macroblock count info->rm = M_RST0; for (i = 0; i < info->ns; i++) dscans[i].dc = 0; } static int dec_readmarker(struct in *in) { int m; in->left = fillbits(in, in->left, in->bits); if ((m = in->marker) == 0) return 0; in->left = 0; in->marker = 0; return m; } static int dec_checkmarker(struct dec_data *decode) { struct jpginfo *info = &decode->info; struct scan *dscans = decode->dscans; struct in *in = &decode->in; int i; if (dec_readmarker(in) != info->rm) return -1; info->nm = info->dri; info->rm = (info->rm + 1) & ~0x08; for (i = 0; i < info->ns; i++) dscans[i].dc = 0; return 0; } static void jpeg_reset_input_context(struct dec_data *decode, unsigned char *buf, int oescap) { /* set input context */ struct in *in = &decode->in; in->p = buf; in->omitescape = oescap; in->left = 0; in->bits = 0; in->marker = 0; } static int dec_rec2(struct in *in, struct dec_hufftbl *hu, int *runp, int c, int i) { int le, bi; le = in->left; bi = in->bits; if (i) { UNGETBITS(in, i & 127); *runp = i >> 8 & 15; i >>= 16; } else { for (i = DECBITS; (c = ((c << 1) | GETBITS(in, 1))) >= (hu->maxcode[i]); i++); if (i >= 16) { in->marker = M_BADHUFF; return 0; } i = hu->vals[hu->valptr[i] + c - hu->maxcode[i - 1] * 2]; *runp = i >> 4; i &= 15; } if (i == 0) { /* sigh, 0xf0 is 11 bit */ LEBI_PUT(in); return 0; } /* receive part */ c = GETBITS(in, i); if (c < (1 << (i - 1))) c += (-1 << i) + 1; LEBI_PUT(in); return c; } #define DEC_REC(in, hu, r, i) ( \ r = GETBITS(in, DECBITS), \ i = hu->llvals[r], \ i & 128 ? \ ( \ UNGETBITS(in, i & 127), \ r = i >> 8 & 15, \ i >> 16 \ ) \ : \ ( \ LEBI_PUT(in), \ i = dec_rec2(in, hu, &r, r, i), \ LEBI_GET(in), \ i \ ) \ ) inline static void decode_mcus(struct in *in, int *dct, int n, struct scan *sc, int *maxp) { struct dec_hufftbl *hu; int i, r, t; int le, bi; memset(dct, 0, n * 64 * sizeof(*dct)); le = in->left; bi = in->bits; while (n-- > 0) { hu = sc->hudc.dhuff; *dct++ = (sc->dc += DEC_REC(in, hu, r, t)); hu = sc->huac.dhuff; i = 63; while (i > 0) { t = DEC_REC(in, hu, r, t); if (t == 0 && r == 0) { dct += i; break; } dct += r; *dct++ = t; i -= r + 1; } *maxp++ = 64 - i; if (n == sc->next) sc++; } LEBI_PUT(in); } static void dec_makehuff(struct dec_hufftbl *hu, int *hufflen, unsigned char *huffvals) { int code, k, i, j, d, x, c, v; for (i = 0; i < (1 << DECBITS); i++) hu->llvals[i] = 0; /* * llvals layout: * * value v already known, run r, backup u bits: * vvvvvvvvvvvvvvvv 0000 rrrr 1 uuuuuuu * value unknown, size b bits, run r, backup u bits: * 000000000000bbbb 0000 rrrr 0 uuuuuuu * value and size unknown: * 0000000000000000 0000 0000 0 0000000 */ code = 0; k = 0; for (i = 0; i < 16; i++, code <<= 1) { /* sizes */ hu->valptr[i] = k; for (j = 0; j < hufflen[i]; j++) { hu->vals[k] = *huffvals++; if (i < DECBITS) { c = code << (DECBITS - 1 - i); v = hu->vals[k] & 0x0f; /* size */ for (d = 1 << (DECBITS - 1 - i); --d >= 0;) { if (v + i < DECBITS) { /* both fit in table */ x = d >> (DECBITS - 1 - v - i); if (v && x < (1 << (v - 1))) x += (-1 << v) + 1; x = x << 16 | (hu->vals[k] & 0xf0) << 4 | (DECBITS - (i + 1 + v)) | 128; } else x = v << 16 | (hu->vals[k] & 0xf0) << 4 | (DECBITS - (i + 1)); hu->llvals[c | d] = x; } } code++; k++; } hu->maxcode[i] = code; } hu->maxcode[16] = 0x20000; /* always terminate decode */ } /****************************************************************/ /************** idct ***************/ /****************************************************************/ #define S22 ((long)IFIX(2 * 0.382683432)) #define C22 ((long)IFIX(2 * 0.923879532)) #define IC4 ((long)IFIX(1 / 0.707106781)) static unsigned char zig2[64] = { 0, 2, 3, 9, 10, 20, 21, 35, 14, 16, 25, 31, 39, 46, 50, 57, 5, 7, 12, 18, 23, 33, 37, 48, 27, 29, 41, 44, 52, 55, 59, 62, 15, 26, 30, 40, 45, 51, 56, 58, 1, 4, 8, 11, 19, 22, 34, 36, 28, 42, 43, 53, 54, 60, 61, 63, 6, 13, 17, 24, 32, 38, 47, 49 }; static void idct(int *in, int *out, int *quant, long off, int max) { long t0, t1, t2, t3, t4, t5, t6, t7; // t ; long tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6; long tmp[64], *tmpp; int i, j, te; unsigned char *zig2p; t0 = off; if (max == 1) { t0 += in[0] * quant[0]; for (i = 0; i < 64; i++) out[i] = ITOINT(t0); return; } zig2p = zig2; tmpp = tmp; for (i = 0; i < 8; i++) { j = *zig2p++; t0 += in[j] * (long) quant[j]; j = *zig2p++; t5 = in[j] * (long) quant[j]; j = *zig2p++; t2 = in[j] * (long) quant[j]; j = *zig2p++; t7 = in[j] * (long) quant[j]; j = *zig2p++; t1 = in[j] * (long) quant[j]; j = *zig2p++; t4 = in[j] * (long) quant[j]; j = *zig2p++; t3 = in[j] * (long) quant[j]; j = *zig2p++; t6 = in[j] * (long) quant[j]; if ((t1 | t2 | t3 | t4 | t5 | t6 | t7) == 0) { tmpp[0 * 8] = t0; tmpp[1 * 8] = t0; tmpp[2 * 8] = t0; tmpp[3 * 8] = t0; tmpp[4 * 8] = t0; tmpp[5 * 8] = t0; tmpp[6 * 8] = t0; tmpp[7 * 8] = t0; tmpp++; t0 = 0; continue; } //IDCT; tmp0 = t0 + t1; t1 = t0 - t1; tmp2 = t2 - t3; t3 = t2 + t3; tmp2 = IMULT(tmp2, IC4) - t3; tmp3 = tmp0 + t3; t3 = tmp0 - t3; tmp1 = t1 + tmp2; tmp2 = t1 - tmp2; tmp4 = t4 - t7; t7 = t4 + t7; tmp5 = t5 + t6; t6 = t5 - t6; tmp6 = tmp5 - t7; t7 = tmp5 + t7; tmp5 = IMULT(tmp6, IC4); tmp6 = IMULT((tmp4 + t6), S22); tmp4 = IMULT(tmp4, (C22 - S22)) + tmp6; t6 = IMULT(t6, (C22 + S22)) - tmp6; t6 = t6 - t7; t5 = tmp5 - t6; t4 = tmp4 - t5; tmpp[0 * 8] = tmp3 + t7; //t0; tmpp[1 * 8] = tmp1 + t6; //t1; tmpp[2 * 8] = tmp2 + t5; //t2; tmpp[3 * 8] = t3 + t4; //t3; tmpp[4 * 8] = t3 - t4; //t4; tmpp[5 * 8] = tmp2 - t5; //t5; tmpp[6 * 8] = tmp1 - t6; //t6; tmpp[7 * 8] = tmp3 - t7; //t7; tmpp++; t0 = 0; } for (i = 0, j = 0; i < 8; i++) { t0 = tmp[j + 0]; t1 = tmp[j + 1]; t2 = tmp[j + 2]; t3 = tmp[j + 3]; t4 = tmp[j + 4]; t5 = tmp[j + 5]; t6 = tmp[j + 6]; t7 = tmp[j + 7]; if ((t1 | t2 | t3 | t4 | t5 | t6 | t7) == 0) { te = ITOINT(t0); out[j + 0] = te; out[j + 1] = te; out[j + 2] = te; out[j + 3] = te; out[j + 4] = te; out[j + 5] = te; out[j + 6] = te; out[j + 7] = te; j += 8; continue; } //IDCT; tmp0 = t0 + t1; t1 = t0 - t1; tmp2 = t2 - t3; t3 = t2 + t3; tmp2 = IMULT(tmp2, IC4) - t3; tmp3 = tmp0 + t3; t3 = tmp0 - t3; tmp1 = t1 + tmp2; tmp2 = t1 - tmp2; tmp4 = t4 - t7; t7 = t4 + t7; tmp5 = t5 + t6; t6 = t5 - t6; tmp6 = tmp5 - t7; t7 = tmp5 + t7; tmp5 = IMULT(tmp6, IC4); tmp6 = IMULT((tmp4 + t6), S22); tmp4 = IMULT(tmp4, (C22 - S22)) + tmp6; t6 = IMULT(t6, (C22 + S22)) - tmp6; t6 = t6 - t7; t5 = tmp5 - t6; t4 = tmp4 - t5; out[j + 0] = ITOINT(tmp3 + t7); out[j + 1] = ITOINT(tmp1 + t6); out[j + 2] = ITOINT(tmp2 + t5); out[j + 3] = ITOINT(t3 + t4); out[j + 4] = ITOINT(t3 - t4); out[j + 5] = ITOINT(tmp2 - t5); out[j + 6] = ITOINT(tmp1 - t6); out[j + 7] = ITOINT(tmp3 - t7); j += 8; } } static unsigned char zig[64] = { 0, 1, 5, 6, 14, 15, 27, 28, 2, 4, 7, 13, 16, 26, 29, 42, 3, 8, 12, 17, 25, 30, 41, 43, 9, 11, 18, 24, 31, 40, 44, 53, 10, 19, 23, 32, 39, 45, 52, 54, 20, 22, 33, 38, 46, 51, 55, 60, 21, 34, 37, 47, 50, 56, 59, 61, 35, 36, 48, 49, 57, 58, 62, 63 }; static int aaidct[8] = { IFIX(0.3535533906), IFIX(0.4903926402), IFIX(0.4619397663), IFIX(0.4157348062), IFIX(0.3535533906), IFIX(0.2777851165), IFIX(0.1913417162), IFIX(0.0975451610) }; inline static void idctqtab(unsigned char *qin, int *qout) { int i, j; for (i = 0; i < 8; i++) for (j = 0; j < 8; j++) qout[zig[i * 8 + j]] = qin[zig[i * 8 + j]] * IMULT(aaidct[i], aaidct[j]); } inline static void scaleidctqtab(int *q, int sc) { int i; for (i = 0; i < 64; i++) q[i] = IMULT(q[i], sc); } /* Reduce to the necessary minimum. FIXME */ static void init_qTable(struct usb_spca50x *spca50x, unsigned int qIndex) { int i, j; /* set up a quantization table */ for (i = 0; i < 2; i++) { for (j = 0; j < 64; j++) { spca50x->maindecode.quant[i][j] = GsmartQTable[qIndex * 2 + i][j]; } } idctqtab(spca50x->maindecode. quant[spca50x->maindecode.dscans[0].tq], spca50x->maindecode.dquant[0]); idctqtab(spca50x->maindecode. quant[spca50x->maindecode.dscans[1].tq], spca50x->maindecode.dquant[1]); idctqtab(spca50x->maindecode. quant[spca50x->maindecode.dscans[2].tq], spca50x->maindecode.dquant[2]); /* rescale qtab */ //scaleidctqtab (spca50x->maindecode.dquant[0], IFIX (0.7)); //scaleidctqtab (spca50x->maindecode.dquant[1], IFIX (0.7)); //scaleidctqtab (spca50x->maindecode.dquant[2], IFIX (0.7)); } void init_jpeg_decoder(struct usb_spca50x *spca50x) { unsigned int i, j, k, l; int tc, th, tt, tac, tdc; const unsigned char *ptr; unsigned int qIndex = spca50x->qindex; memcpy(spca50x->maindecode.comps, comp_template, MAXCOMP * sizeof(struct comp)); /* set up the huffman table */ ptr = GsmartJPEGHuffmanTable; l = GSMART_JPG_HUFFMAN_TABLE_LENGTH; while (l > 0) { int hufflen[16]; unsigned char huffvals[256]; tc = *ptr++; th = tc & 15; tc >>= 4; tt = tc * 2 + th; if (tc > 1 || th > 1) { //printf("died whilst setting up huffman table.\n"); //abort(); } for (i = 0; i < 16; i++) hufflen[i] = *ptr++; l -= 1 + 16; k = 0; for (i = 0; i < 16; i++) { for (j = 0; j < (unsigned int) hufflen[i]; j++) huffvals[k++] = *ptr++; l -= hufflen[i]; } dec_makehuff(dhuff + tt, hufflen, huffvals); } /* set up the scan table */ ptr = GsmartJPEGScanTable; for (i = 0; i < 3; i++) { spca50x->maindecode.dscans[i].cid = *ptr++; tdc = *ptr++; tac = tdc & 15; tdc >>= 4; if (tdc > 1 || tac > 1) { //printf("died whilst setting up scan table.\n"); //abort(); } /* for each component */ for (j = 0; j < 3; j++) if (spca50x->maindecode.comps[j].cid == spca50x->maindecode.dscans[i].cid) break; spca50x->maindecode.dscans[i].hv = spca50x->maindecode.comps[j].hv; spca50x->maindecode.dscans[i].tq = spca50x->maindecode.comps[j].tq; spca50x->maindecode.dscans[i].hudc.dhuff = dec_huffdc + tdc; spca50x->maindecode.dscans[i].huac.dhuff = dec_huffac + tac; } if (spca50x->maindecode.dscans[0].cid != 1 || spca50x->maindecode.dscans[1].cid != 2 || spca50x->maindecode.dscans[2].cid != 3) { //printf("invalid cid found.\n"); //abort(); } if (spca50x->maindecode.dscans[0].hv != 0x22 || spca50x->maindecode.dscans[1].hv != 0x11 || spca50x->maindecode.dscans[2].hv != 0x11) { //printf("invalid hv found.\n"); //abort(); } spca50x->maindecode.dscans[0].next = 6 - 4; spca50x->maindecode.dscans[1].next = 6 - 4 - 1; spca50x->maindecode.dscans[2].next = 6 - 4 - 1 - 1; /* 411 encoding */ /* set up a quantization table */ init_qTable(spca50x, qIndex); } static int bgr = 0; /* Gamma correction setting */ /* Gtable[0][n] -> 2.2 * Gtable[1][n] -> 1.7 * Gtable[2][n] -> 1.45 * Gtable[3][n] -> 1 * Gtable[4][n] -> 0.6896 * Gtable[5][n] -> 0.5882 * Gtable[6][n] -> 0.4545 * gCor coeff 0..6 */ int spca50x_outpicture(struct spca50x_frame *myframe) { /* general idea keep a frame in the temporary buffer from the tasklet */ /* decode with native format at input and asked format at output */ /* myframe->cameratype is the native input format */ /* myframe->format is the asked format */ struct pictparam *gCorrect = &myframe->pictsetting; unsigned char *red = myframe->decoder->Red; unsigned char *green = myframe->decoder->Green; unsigned char *blue = myframe->decoder->Blue; int width = 0; int height = 0; int done = 0; int i; if (gCorrect->change) { if (gCorrect->change == 0x01) { /* Gamma setting change compute all case */ memcpy(red, >able[gCorrect->gamma], 256); memcpy(green, >able[gCorrect->gamma], 256); memcpy(blue, >able[gCorrect->gamma], 256); for (i = 0; i < 256; i++) { red[i] = CLIP(((red[i] + gCorrect->OffRed) * gCorrect->GRed) >> 8); green[i] = CLIP(((green[i] + gCorrect->OffGreen) * gCorrect->GGreen) >> 8); blue[i] = CLIP(((blue[i] + gCorrect->OffBlue) * gCorrect->GBlue) >> 8); } bgr = gCorrect->force_rgb; gCorrect->change = 0x00; } if (gCorrect->change == 0x02) { /* Red setting change compute Red Value */ memcpy(red, >able[gCorrect->gamma], 256); for (i = 0; i < 256; i++) { red[i] = CLIP(((red[i] + gCorrect->OffRed) * gCorrect->GRed) >> 8); } gCorrect->change &= ~0x02; } if (gCorrect->change == 0x04) { /* Green setting change compute Green Value */ memcpy(green, >able[gCorrect->gamma], 256); for (i = 0; i < 256; i++) { green[i] = CLIP(((green[i] + gCorrect->OffGreen) * gCorrect->GGreen) >> 8); } gCorrect->change &= ~0x04; } if (gCorrect->change == 0x08) { /* Blue setting change compute Blue Value */ memcpy(blue, >able[gCorrect->gamma], 256); for (i = 0; i < 256; i++) { blue[i] = CLIP(((blue[i] + gCorrect->OffBlue) * gCorrect->GBlue) >> 8); } gCorrect->change &= ~0x08; } if (gCorrect->change == 0x10) { /* force_rgb setting change */ bgr = gCorrect->force_rgb; gCorrect->change &= ~0x10; } } printf("outpict cameratype %d\n", myframe->cameratype); switch (myframe->cameratype) { case JPGC: height = (myframe->data[11] << 8) | myframe->data[12]; width = (myframe->data[13] << 8) | myframe->data[14]; if (myframe->hdrheight != height || myframe->hdrwidth != width) done = ERR_CORRUPTFRAME; else { //set info.dri struct should be kmalloc with the // instance camera myframe->decoder->info.dri = myframe->data[5]; if (myframe->format == VIDEO_PALETTE_JPEG) { memcpy(myframe->tmpbuffer, myframe->data, myframe->scanlength); done = make_jpeg_conexant(myframe); } else { memcpy(myframe->tmpbuffer, myframe->data + 39, myframe->scanlength - 39); done = jpeg_decode422(myframe, bgr); } } break; case JPGH: printf("outpict, format jpgh\n"); width = (myframe->data[10] << 8) | myframe->data[11]; height = (myframe->data[12] << 8) | myframe->data[13]; /* some camera did not respond with the good height ie:Labtec Pro 240 -> 232 */ if (myframe->hdrwidth != width) { printf("corrupt frame, hdrwidth %d width %d\n", myframe->hdrwidth, width); done = ERR_CORRUPTFRAME; } else { // reset info.dri myframe->decoder->info.dri = 0; memcpy(myframe->tmpbuffer, myframe->data + 16, myframe->scanlength - 16); if (myframe->format == VIDEO_PALETTE_JPEG) done = make_jpeg(myframe); else done = jpeg_decode422(myframe, bgr); } break; case JPGM: case JPGS: // reset info.dri myframe->decoder->info.dri = 0; memcpy(myframe->tmpbuffer, myframe->data, myframe->scanlength); if (myframe->format == VIDEO_PALETTE_JPEG) done = make_jpeg(myframe); else done = jpeg_decode422(myframe, bgr); break; case JPEG: memcpy(myframe->tmpbuffer, myframe->data, myframe->scanlength); if (myframe->format == VIDEO_PALETTE_JPEG) done = make_jpeg(myframe); else done = jpeg_decode411(myframe, bgr); break; case YUVY: case YUYV: case YYUV: memcpy(myframe->tmpbuffer, myframe->data, myframe->scanlength); done = yuv_decode(myframe, bgr); break; case PGBRG: done = pixart_decompress(myframe); if (done < 0) break; done = bayer_decode(myframe, bgr); break; case GBGR: /* translate the tv8532 stream into GBRG stream */ tv8532_preprocess(myframe); done = bayer_decode(myframe, bgr); break; case GBRG: memcpy(myframe->tmpbuffer, myframe->data, myframe->scanlength); done = bayer_decode(myframe, bgr); break; case S561: if (myframe->data[1] & 0x10) decode_spca561(myframe->data, myframe->tmpbuffer, myframe->width, myframe->height); else memcpy(myframe->tmpbuffer, myframe->data + 20, myframe->scanlength); done = bayer_decode(myframe, bgr); break; case SN9C: sonix_decompress(myframe); done = bayer_decode(myframe, bgr); break; default: done = -1; break; } return done; } static int yuv_decode(struct spca50x_frame *myframe, int force_rgb) { int r_offset, g_offset, b_offset; int my, mx; /* scan input surface */ unsigned char *pic1; /* output surface */ __u16 *pix1, *pix2; /* same for 16 bits output */ unsigned char *U, *V; /* chroma output pointer */ int inuv, inv, pocx; /* offset chroma input */ int iny, iny1; /* offset luma input */ int nextinline, nextoutline; int u1, v1, rg; unsigned char y, y1; char u, v; unsigned char *pic = myframe->data; /* output surface */ unsigned char *buf = myframe->tmpbuffer; /* input surface */ int width = myframe->hdrwidth; int height = myframe->hdrheight; int softwidth = myframe->width; int softheight = myframe->height; //int method = myframe->method; int format = myframe->format; int cropx1 = myframe->cropx1; int cropx2 = myframe->cropx2; int cropy1 = myframe->cropy1; int cropy2 = myframe->cropy2; unsigned char *red = myframe->decoder->Red; unsigned char *green = myframe->decoder->Green; unsigned char *blue = myframe->decoder->Blue; int bpp; int framesize, frameUsize; framesize = softwidth * softheight; frameUsize = framesize >> 2; /* rgb or bgr like U or V that's the question */ if (force_rgb) { U = pic + framesize; V = U + frameUsize; r_offset = 2; g_offset = 1; b_offset = 0; } else { V = pic + framesize; U = V + frameUsize; r_offset = 0; g_offset = 1; b_offset = 2; } switch (myframe->cameratype) { case YUVY:{ iny = 0; /********* iny **********/ inuv = width; /*** inuv **** inv ******/ nextinline = 3 * width; inv = (nextinline >> 1); iny1 = width << 1; /********* iny1 *********/ } break; case YUYV:{ iny = 0; /********* iny **********/ inuv = width; /*** inuv **** iny1 *****/ nextinline = 3 * width; iny1 = (nextinline >> 1); inv = iny1 + width; /*** iny1 **** inv ******/ } break; case YYUV:{ iny = 0; /********* iny **********/ iny1 = width; /********* iny1 *********/ inuv = width << 1; /*** inuv **** inv ******/ inv = inuv + (width >> 1); nextinline = 3 * width; } break; default:{ iny = 0; /* make compiler happy */ iny1 = 0; inuv = 0; inv = 0; nextinline = 0; } break; } /* Decode to the correct format. */ switch (format) { case VIDEO_PALETTE_RGB565: { bpp = 2; /* initialize */ pix1 = (__u16 *) pic; pix2 = pix1 + softwidth; for (my = 0; my < height; my += 2) { for (mx = 0, pocx = 0; mx < width; mx += 2, pocx++) { /* test if we need to decode */ if ((my >= cropy1) && (my < height - cropy2) && (mx >= cropx1) && (mx < width - cropx2)) { /* yes decode */ if (force_rgb) { u = buf[inuv + pocx]; v = buf[inv + pocx]; } else { v = buf[inuv + pocx]; u = buf[inv + pocx]; } v1 = ((v << 10) + (v << 9)) >> 10; rg = ((u << 8) + (u << 7) + (v << 9) + (v << 4)) >> 10; u1 = ((u << 11) + (u << 4)) >> 10; /* top pixel Right */ y1 = 128 + buf[iny + mx]; *pix1++ = ((red[CLIP((y1 + v1))] & 0xF8) >> 3 | ((green [CLIP((y1 - rg))] & 0xFC) << 3) | ((blue[CLIP((y1 + u1))] & 0xF8) << 8)); /* top pixel Left */ y1 = 128 + buf[iny + mx + 1]; *pix1++ = ((red[CLIP((y1 + v1))] & 0xF8) >> 3 | ((green [CLIP((y1 - rg))] & 0xFC) << 3) | ((blue[CLIP((y1 + u1))] & 0xF8) << 8)); /* bottom pixel Right */ y1 = 128 + buf[iny1 + mx]; *pix2++ = ((red[CLIP((y1 + v1))] & 0xF8) >> 3 | ((green [CLIP((y1 - rg))] & 0xFC) << 3) | ((blue[CLIP((y1 + u1))] & 0xF8) << 8)); /* bottom pixel Left */ y1 = 128 + buf[iny1 + mx + 1]; *pix2++ = ((red[CLIP((y1 + v1))] & 0xF8) >> 3 | ((green [CLIP((y1 - rg))] & 0xFC) << 3) | ((blue[CLIP((y1 + u1))] & 0xF8) << 8)); } // end test decode } // end mx loop iny += nextinline; inuv += nextinline; inv += nextinline; iny1 += nextinline; if (my >= cropy1) { /* are we in a decode surface move the output pointer */ pix1 += softwidth; pix2 += softwidth; } } // end my loop } myframe->scanlength = (long) (softwidth * softheight * bpp); break; case VIDEO_PALETTE_RGB32: case VIDEO_PALETTE_RGB24: { bpp = (format == VIDEO_PALETTE_RGB32) ? 4 : 3; /* initialize */ nextoutline = bpp * softwidth; pic1 = pic + nextoutline; for (my = 0; my < height; my += 2) { for (mx = 0, pocx = 0; mx < width; mx += 2, pocx++) { /* test if we need to decode */ if ((my >= cropy1) && (my < height - cropy2) && (mx >= cropx1) && (mx < width - cropx2)) { /* yes decode */ v = buf[inuv + pocx]; u = buf[inv + pocx]; v1 = ((v << 10) + (v << 9)) >> 10; rg = ((u << 8) + (u << 7) + (v << 9) + (v << 4)) >> 10; u1 = ((u << 11) + (u << 4)) >> 10; y = 128 + buf[iny + mx]; /* top pixel Right */ pic[r_offset] = red[CLIP((y + v1))]; pic[g_offset] = green[CLIP((y - rg))]; pic[b_offset] = blue[CLIP((y + u1))]; pic += bpp; /* top pixel Left */ y = 128 + buf[iny + mx + 1]; pic[r_offset] = red[CLIP((y + v1))]; pic[g_offset] = green[CLIP((y - rg))]; pic[b_offset] = blue[CLIP((y + u1))]; pic += bpp; /* bottom pixel Right */ y1 = 128 + buf[iny1 + mx]; pic1[r_offset] = red[CLIP((y1 + v1))]; pic1[g_offset] = green[CLIP((y1 - rg))]; pic1[b_offset] = blue[CLIP((y1 + u1))]; pic1 += bpp; /* bottom pixel Left */ y1 = 128 + buf[iny1 + mx + 1]; pic1[r_offset] = red[CLIP((y1 + v1))]; pic1[g_offset] = green[CLIP((y1 - rg))]; pic1[b_offset] = blue[CLIP((y1 + u1))]; pic1 += bpp; } // end test decode } // end mx loop iny += nextinline; inuv += nextinline; inv += nextinline; iny1 += nextinline; if (my >= cropy1) { /* are we in a decode surface move the output pointer */ pic += nextoutline; pic1 += nextoutline; } } // end my loop } myframe->scanlength = (long) (softwidth * softheight * bpp); break; case VIDEO_PALETTE_YUV420P: { /* initialize */ pic1 = pic + softwidth; for (my = 0; my < height; my += 2) { for (mx = 0, pocx = 0; mx < width; mx += 2, pocx++) { /* test if we need to decode */ if ((my >= cropy1) && (my < height - cropy2) && (mx >= cropx1) && (mx < width - cropx2)) { /* yes decode */ *V++ = 128 + buf[inuv + pocx]; *U++ = 128 + buf[inv + pocx]; *pic++ = 128 + buf[iny + mx]; *pic++ = 128 + buf[iny + mx + 1]; *pic1++ = 128 + buf[iny1 + mx]; *pic1++ = 128 + buf[iny1 + mx + 1]; } // end test decode } // end mx loop iny += nextinline; inuv += nextinline; inv += nextinline; iny1 += nextinline; if (my >= cropy1) { /* are we in a decode surface move the output pointer */ pic += softwidth; pic1 += softwidth; } } // end my loop } myframe->scanlength = (long) (softwidth * softheight * 3) >> 1; break; case VIDEO_PALETTE_YUYV: bpp = 2; nextoutline = bpp * softwidth; pic1 = pic + nextoutline; for (my = 0; my < height; my += 2) { for (mx = 0, pocx = 0; mx < width; mx += 2, pocx++) { /* test if we need to decode */ if ((my >= cropy1) && (my < height - cropy2) && (mx >= cropx1) && (mx < width - cropx2)) { /* yes decode */ *pic++ = 128 + buf[iny + mx]; *pic++ = 128 + buf[inuv + pocx]; //V *pic++ = 128 + buf[iny + mx + 1]; *pic++ = 128 + buf[inv + pocx]; //U *pic1++ = 128 + buf[iny1 + mx]; *pic1++ = 128 + buf[inuv + pocx]; //V *pic1++ = 128 + buf[iny1 + mx + 1]; *pic++ = 128 + buf[inv + pocx]; //U } // end test decode } // end mx loop iny += nextinline; inuv += nextinline; inv += nextinline; iny1 += nextinline; if (my >= cropy1) { /* are we in a decode surface move the output pointer */ pic += nextoutline; pic1 += nextoutline; } } // end my loop myframe->scanlength = (long) (softwidth * softheight * 2); break; default: break; } // end case return 0; } /* * linux/drivers/video/fbcon-jpegdec.c - a tiny jpeg decoder. * * (w) August 2001 by Michael Schroeder, * * I severly gutted this beast and hardcoded it to the palette and subset * of jpeg needed for the spca50x driver. Also converted it from K&R style * C to a more modern form ;). Michael can't be blamed for what is left. * All nice features are his, all bugs are mine. - till * * Change color space converter for YUVP and RGB - * Rework the IDCT implementation for best speed, cut test in the loop but instead * more copy and paste code :) * For more details about idct look at : * http://rnvs.informatik.tu-chemnitz.de/~jan/MPEG/HTML/IDCT.html * 12/12/2003 mxhaard@magic.fr * add make jpeg from header (mxhaard 20/09/2004) * add jpeg_decode for 422 stream (mxhaard 01/10/2004) */ static int jpeg_decode411(struct spca50x_frame *myframe, int force_rgb) { int mcusx, mcusy, mx, my; int *dcts = myframe->dcts; int *out = myframe->out; int *max = myframe->max; // int i; int bpp; int framesize, frameUsize; int k, j; int nextline, nextuv, nextblk, nextnewline; unsigned char *pic0, *pic1, *outv, *outu; __u16 *pix1, *pix2; int picy, picx, pocx, pocy; unsigned char *U, *V; int *outy, *inv, *inu; int outy1, outy2; int v, u, y1, v1, u1, u2; int r_offset, g_offset, b_offset; unsigned char *pic = myframe->data; /* output surface */ unsigned char *buf = myframe->tmpbuffer; /* input surface */ int width = myframe->hdrwidth; int height = myframe->hdrheight; int softwidth = myframe->width; int softheight = myframe->height; //int method = myframe->method; int format = myframe->format; int cropx1 = myframe->cropx1; int cropx2 = myframe->cropx2; int cropy1 = myframe->cropy1; int cropy2 = myframe->cropy2; unsigned char *red = myframe->decoder->Red; unsigned char *green = myframe->decoder->Green; unsigned char *blue = myframe->decoder->Blue; struct dec_data *decode = myframe->decoder; if ((height & 15) || (width & 15)) return 1; if (width < softwidth || height < softheight) return 1; mcusx = width >> 4; mcusy = height >> 4; framesize = softwidth * softheight; frameUsize = framesize >> 2; jpeg_reset_input_context(decode, buf, 0); /* for each component. Reset dc values. */ //for (i = 0; i < 3; i++) //dscans[i].dc = 0; dec_initscans(decode); /* rgb or bgr like U or V that's the question */ if (force_rgb) { U = pic + framesize; V = U + frameUsize; r_offset = 2; g_offset = 1; b_offset = 0; } else { V = pic + framesize; U = V + frameUsize; r_offset = 0; g_offset = 1; b_offset = 2; } /* Decode to the correct format. */ switch (format) { case VIDEO_PALETTE_RGB565: { bpp = 2; nextline = ((softwidth << 1) - 16); // *bpp; nextblk = bpp * (softwidth << 4); nextnewline = softwidth; // *bpp; for (my = 0, picy = 0; my < mcusy; my++) { for (mx = 0, picx = 0; mx < mcusx; mx++) { decode_mcus(&decode->in, dcts, 6, decode->dscans, max); if ((my >= cropy1) && (my < mcusy - cropy2) && (mx >= cropx1) && (mx < mcusx - cropx2)) { idct(dcts, out, decode->dquant[0], IFIX(128.5), max[0]); idct(dcts + 64, out + 64, decode->dquant[0], IFIX(128.5), max[1]); idct(dcts + 128, out + 128, decode->dquant[0], IFIX(128.5), max[2]); idct(dcts + 192, out + 192, decode->dquant[0], IFIX(128.5), max[3]); idct(dcts + 256, out + 256, decode->dquant[1], IFIX(0.5), max[4]); idct(dcts + 320, out + 320, decode->dquant[2], IFIX(0.5), max[5]); pix1 = (__u16 *) (pic + picx + picy); pix2 = pix1 + nextnewline; outy = out; outy1 = 0; outy2 = 8; inv = out + 64 * 4; inu = out + 64 * 5; for (j = 0; j < 8; j++) { for (k = 0; k < 8; k++) { if (k == 4) { outy1 += 56; outy2 += 56; } /* outup 4 pixels */ /* get the UV colors need to change UV order for force rgb? */ if (force_rgb) { u = *inv++; v = *inu++; } else { v = *inv++; u = *inu++; } /* MX color space why not? */ v1 = ((v << 10) + (v << 9)) >> 10; u1 = ((u << 8) + (u << 7) + (v << 9) + (v << 4)) >> 10; u2 = ((u << 11) + (u << 4)) >> 10; /* top pixel Right */ y1 = outy[outy1++]; *pix1++ = ((red[CLIP((y1 + v1))] & 0xF8) >> 3 | ((green[CLIP((y1 - u1))] & 0xFC) << 3) | ((blue[CLIP((y1 + u2))] & 0xF8) << 8)); /* top pixel Left */ y1 = outy[outy1++]; *pix1++ = ((red[CLIP((y1 + v1))] & 0xF8) >> 3 | ((green[CLIP((y1 - u1))] & 0xFC) << 3) | ((blue[CLIP((y1 + u2))] & 0xF8) << 8)); /* bottom pixel Right */ y1 = outy[outy2++]; *pix2++ = ((red[CLIP((y1 + v1))] & 0xF8) >> 3 | ((green[CLIP((y1 - u1))] & 0xFC) << 3) | ((blue[CLIP((y1 + u2))] & 0xF8) << 8)); /* bottom pixel Left */ y1 = outy[outy2++]; *pix2++ = ((red[CLIP((y1 + v1))] & 0xF8) >> 3 | ((green[CLIP((y1 - u1))] & 0xFC) << 3) | ((blue[CLIP((y1 + u2))] & 0xF8) << 8)); } if (j == 3) { outy = out + 128; } else { outy += 16; } outy1 = 0; outy2 = 8; pix1 += nextline; pix2 += nextline; } picx += 16 * bpp; } } if (my >= cropy1) picy += nextblk; } } myframe->scanlength = (long) (softwidth * softheight * bpp); break; case VIDEO_PALETTE_RGB32: case VIDEO_PALETTE_RGB24: { bpp = (format == VIDEO_PALETTE_RGB32) ? 4 : 3; nextline = bpp * ((softwidth << 1) - 16); nextblk = bpp * (softwidth << 4); nextnewline = bpp * softwidth; for (my = 0, picy = 0; my < mcusy; my++) { for (mx = 0, picx = 0; mx < mcusx; mx++) { decode_mcus(&decode->in, dcts, 6, decode->dscans, max); if ((my >= cropy1) && (my < mcusy - cropy2) && (mx >= cropx1) && (mx < mcusx - cropx2)) { idct(dcts, out, decode->dquant[0], IFIX(128.5), max[0]); idct(dcts + 64, out + 64, decode->dquant[0], IFIX(128.5), max[1]); idct(dcts + 128, out + 128, decode->dquant[0], IFIX(128.5), max[2]); idct(dcts + 192, out + 192, decode->dquant[0], IFIX(128.5), max[3]); idct(dcts + 256, out + 256, decode->dquant[1], IFIX(0.5), max[4]); idct(dcts + 320, out + 320, decode->dquant[2], IFIX(0.5), max[5]); pic0 = pic + picx + picy; pic1 = pic0 + nextnewline; outy = out; outy1 = 0; outy2 = 8; inv = out + 64 * 4; inu = out + 64 * 5; for (j = 0; j < 8; j++) { for (k = 0; k < 8; k++) { if (k == 4) { outy1 += 56; outy2 += 56; } /* outup 4 pixels */ /* get the UV colors need to change UV order for force rgb? */ v = *inv++; u = *inu++; /* MX color space why not? */ v1 = ((v << 10) + (v << 9)) >> 10; u1 = ((u << 8) + (u << 7) + (v << 9) + (v << 4)) >> 10; u2 = ((u << 11) + (u << 4)) >> 10; /* top pixel Right */ y1 = outy[outy1++]; pic0[r_offset] = red[CLIP((y1 + v1))]; pic0[g_offset] = green[CLIP((y1 - u1))]; pic0[b_offset] = blue[CLIP((y1 + u2))]; pic0 += bpp; /* top pixel Left */ y1 = outy[outy1++]; pic0[r_offset] = red[CLIP((y1 + v1))]; pic0[g_offset] = green[CLIP((y1 - u1))]; pic0[b_offset] = blue[CLIP((y1 + u2))]; pic0 += bpp; /* bottom pixel Right */ y1 = outy[outy2++]; pic1[r_offset] = red[CLIP((y1 + v1))]; pic1[g_offset] = green[CLIP((y1 - u1))]; pic1[b_offset] = blue[CLIP((y1 + u2))]; pic1 += bpp; /* bottom pixel Left */ y1 = outy[outy2++]; pic1[r_offset] = red[CLIP((y1 + v1))]; pic1[g_offset] = green[CLIP((y1 - u1))]; pic1[b_offset] = blue[CLIP((y1 + u2))]; pic1 += bpp; } if (j == 3) { outy = out + 128; } else { outy += 16; } outy1 = 0; outy2 = 8; pic0 += nextline; pic1 += nextline; } picx += 16 * bpp; } } if (my >= cropy1) picy += nextblk; } } myframe->scanlength = (long) (softwidth * softheight * bpp); break; case VIDEO_PALETTE_YUV420P: { nextline = (softwidth << 1) - 16; nextuv = (softwidth >> 1) - 8; nextblk = softwidth << 4; nextnewline = softwidth << 2; for (my = 0, picy = 0, pocy = 0; my < mcusy; my++) { for (mx = 0, picx = 0, pocx = 0; mx < mcusx; mx++) { decode_mcus(&decode->in, dcts, 6, decode->dscans, max); if ((my >= cropy1) && (my < mcusy - cropy2) && (mx >= cropx1) && (mx < mcusx - cropx2)) { idct(dcts, out, decode->dquant[0], IFIX(128.5), max[0]); idct(dcts + 64, out + 64, decode->dquant[0], IFIX(128.5), max[1]); idct(dcts + 128, out + 128, decode->dquant[0], IFIX(128.5), max[2]); idct(dcts + 192, out + 192, decode->dquant[0], IFIX(128.5), max[3]); idct(dcts + 256, out + 256, decode->dquant[1], IFIX(0.5), max[4]); idct(dcts + 320, out + 320, decode->dquant[2], IFIX(0.5), max[5]); pic0 = pic + picx + picy; pic1 = pic0 + softwidth; outv = V + (pocx + pocy); outu = U + (pocx + pocy); outy = out; outy1 = 0; outy2 = 8; inv = out + 64 * 4; inu = out + 64 * 5; for (j = 0; j < 8; j++) { for (k = 0; k < 8; k++) { if (k == 4) { outy1 += 56; outy2 += 56; } /* outup 4 pixels */ *pic0++ = CLIP(outy[outy1]); outy1++; *pic0++ = CLIP(outy[outy1]); outy1++; *pic1++ = CLIP(outy[outy2]); outy2++; *pic1++ = CLIP(outy[outy2]); outy2++; *outv++ = CLIP(128 + *inv); inv++; *outu++ = CLIP(128 + *inu); inu++; } if (j == 3) { outy = out + 128; } else { outy += 16; } outy1 = 0; outy2 = 8; pic0 += nextline; pic1 += nextline; outv += nextuv; outu += nextuv; } picx += 16; pocx += 8; } } if (my >= cropy1) { picy += nextblk; pocy += nextnewline; } } } myframe->scanlength = (long) ((softwidth * softheight * 3) >> 1); break; default: break; } // end case return 0; } static int jpeg_decode422(struct spca50x_frame *myframe, int force_rgb) { int mcusx, mcusy, mx, my; int *dcts = myframe->dcts; int *out = myframe->out; int *max = myframe->max; int bpp; int framesize, frameUsize; int k, j; int nextline, nextuv, nextblk, nextnewline; unsigned char *pic0, *pic1, *outv, *outu; __u16 *pix1, *pix2; int picy, picx, pocx, pocy; unsigned char *U, *V; int *outy, *inv, *inu; int outy1, outy2; int v, u, y1, v1, u1, u2; int r_offset, g_offset, b_offset; unsigned char *pic = myframe->data; /* output surface */ unsigned char *buf = myframe->tmpbuffer; /* input surface */ int width = myframe->hdrwidth; int height = myframe->hdrheight; int softwidth = myframe->width; int softheight = myframe->height; //int method = myframe->method; int format = myframe->format; int cropx1 = myframe->cropx1; int cropx2 = myframe->cropx2; int cropy1 = myframe->cropy1; int cropy2 = myframe->cropy2; unsigned char *red = myframe->decoder->Red; unsigned char *green = myframe->decoder->Green; unsigned char *blue = myframe->decoder->Blue; struct dec_data *decode = myframe->decoder; if ((height & 15) || (width & 7)) return 1; if (width < softwidth || height < softheight) return 1; mcusx = width >> 4; mcusy = height >> 3; framesize = softwidth * softheight; frameUsize = framesize >> 2; jpeg_reset_input_context(decode, buf, 1); /* for each component. Reset dc values. */ dec_initscans(decode); /* rgb or bgr like U or V that's the question */ if (force_rgb) { U = pic + framesize; V = U + frameUsize; r_offset = 2; g_offset = 1; b_offset = 0; } else { V = pic + framesize; U = V + frameUsize; r_offset = 0; g_offset = 1; b_offset = 2; } printf("in %s line %d palette %d\n", __FUNCTION__, __LINE__, format); /* Decode to the correct format. */ switch (format) { case VIDEO_PALETTE_RGB565: { bpp = 2; nextline = ((softwidth << 1) - 16); // *bpp; nextblk = bpp * (softwidth << 3); nextnewline = softwidth; // *bpp; for (my = 0, picy = 0; my < mcusy; my++) { for (mx = 0, picx = 0; mx < mcusx; mx++) { if (decode->info.dri && !--decode->info.nm) if (dec_checkmarker(decode)) return ERR_WRONG_MARKER; decode_mcus(&decode->in, dcts, 4, decode->dscans, max); if ((my >= cropy1) && (my < mcusy - cropy2) && (mx >= cropx1) && (mx < mcusx - cropx2)) { idct(dcts, out, decode->dquant[0], IFIX(128.5), max[0]); idct(dcts + 64, out + 64, decode->dquant[0], IFIX(128.5), max[1]); idct(dcts + 128, out + 256, decode->dquant[1], IFIX(0.5), max[2]); idct(dcts + 192, out + 320, decode->dquant[2], IFIX(0.5), max[3]); pix1 = (__u16 *) (pic + picx + picy); pix2 = pix1 + nextnewline; outy = out; outy1 = 0; outy2 = 8; inv = out + 64 * 4; inu = out + 64 * 5; for (j = 0; j < 4; j++) { for (k = 0; k < 8; k++) { if (k == 4) { outy1 += 56; outy2 += 56; } /* outup 4 pixels Colors are treated as 411 */ /* get the UV colors need to change UV order for force rgb? */ if (force_rgb) { u = *inv++; v = *inu++; } else { v = *inv++; u = *inu++; } /* MX color space why not? */ v1 = ((v << 10) + (v << 9)) >> 10; u1 = ((u << 8) + (u << 7) + (v << 9) + (v << 4)) >> 10; u2 = ((u << 11) + (u << 4)) >> 10; /* top pixel Right */ y1 = outy[outy1++]; *pix1++ = ((red[CLIP((y1 + v1))] & 0xF8) >> 3 | ((green[CLIP((y1 - u1))] & 0xFC) << 3) | ((blue[CLIP((y1 + u2))] & 0xF8) << 8)); /* top pixel Left */ y1 = outy[outy1++]; *pix1++ = ((red[CLIP((y1 + v1))] & 0xF8) >> 3 | ((green[CLIP((y1 - u1))] & 0xFC) << 3) | ((blue[CLIP((y1 + u2))] & 0xF8) << 8)); /* bottom pixel Right */ y1 = outy[outy2++]; *pix2++ = ((red[CLIP((y1 + v1))] & 0xF8) >> 3 | ((green[CLIP((y1 - u1))] & 0xFC) << 3) | ((blue[CLIP((y1 + u2))] & 0xF8) << 8)); /* bottom pixel Left */ y1 = outy[outy2++]; *pix2++ = ((red[CLIP((y1 + v1))] & 0xF8) >> 3 | ((green[CLIP((y1 - u1))] & 0xFC) << 3) | ((blue[CLIP((y1 + u2))] & 0xF8) << 8)); } outy += 16; outy1 = 0; outy2 = 8; pix1 += nextline; pix2 += nextline; } picx += 16 * bpp; } } if (my >= cropy1) picy += nextblk; } } myframe->scanlength = (long) (softwidth * softheight * bpp); break; case VIDEO_PALETTE_RGB32: case VIDEO_PALETTE_RGB24: { bpp = (format == VIDEO_PALETTE_RGB32) ? 4 : 3; nextline = bpp * ((softwidth << 1) - 16); nextblk = bpp * (softwidth << 3); nextnewline = bpp * softwidth; for (my = 0, picy = 0; my < mcusy; my++) { for (mx = 0, picx = 0; mx < mcusx; mx++) { if (decode->info.dri && !--decode->info.nm) if (dec_checkmarker(decode)) return ERR_WRONG_MARKER; decode_mcus(&decode->in, dcts, 4, decode->dscans, max); if ((my >= cropy1) && (my < mcusy - cropy2) && (mx >= cropx1) && (mx < mcusx - cropx2)) { idct(dcts, out, decode->dquant[0], IFIX(128.5), max[0]); idct(dcts + 64, out + 64, decode->dquant[0], IFIX(128.5), max[1]); idct(dcts + 128, out + 256, decode->dquant[1], IFIX(0.5), max[2]); idct(dcts + 192, out + 320, decode->dquant[2], IFIX(0.5), max[3]); pic0 = pic + picx + picy; pic1 = pic0 + nextnewline; outy = out; outy1 = 0; outy2 = 8; inv = out + 64 * 4; inu = out + 64 * 5; for (j = 0; j < 4; j++) { for (k = 0; k < 8; k++) { if (k == 4) { outy1 += 56; outy2 += 56; } /* outup 4 pixels Colors are treated as 411 */ v = *inv++; u = *inu++; /* MX color space why not? */ v1 = ((v << 10) + (v << 9)) >> 10; u1 = ((u << 8) + (u << 7) + (v << 9) + (v << 4)) >> 10; u2 = ((u << 11) + (u << 4)) >> 10; /* top pixel Right */ y1 = outy[outy1++]; pic0[r_offset] = red[CLIP((y1 + v1))]; pic0[g_offset] = green[CLIP((y1 - u1))]; pic0[b_offset] = blue[CLIP((y1 + u2))]; pic0 += bpp; /* top pixel Left */ y1 = outy[outy1++]; pic0[r_offset] = red[CLIP((y1 + v1))]; pic0[g_offset] = green[CLIP((y1 - u1))]; pic0[b_offset] = blue[CLIP((y1 + u2))]; pic0 += bpp; /* bottom pixel Right */ y1 = outy[outy2++]; pic1[r_offset] = red[CLIP((y1 + v1))]; pic1[g_offset] = green[CLIP((y1 - u1))]; pic1[b_offset] = blue[CLIP((y1 + u2))]; pic1 += bpp; /* bottom pixel Left */ y1 = outy[outy2++]; pic1[r_offset] = red[CLIP((y1 + v1))]; pic1[g_offset] = green[CLIP((y1 - u1))]; pic1[b_offset] = blue[CLIP((y1 + u2))]; pic1 += bpp; } outy += 16; outy1 = 0; outy2 = 8; pic0 += nextline; pic1 += nextline; } picx += 16 * bpp; } } if (my >= cropy1) picy += nextblk; } } myframe->scanlength = (long) (softwidth * softheight * bpp); break; case VIDEO_PALETTE_YUV420P: { printf("in %s line %d mcusx %d mcusy %d\n", __FUNCTION__, __LINE__, mcusx, mcusy); nextline = (softwidth << 1) - 16; nextuv = (softwidth >> 1) - 8; nextblk = softwidth << 3; nextnewline = softwidth << 1; //2 for (my = 0, picy = 0, pocy = 0; my < mcusy; my++) { for (mx = 0, picx = 0, pocx = 0; mx < mcusx; mx++) { if (decode->info.dri && !--decode->info.nm) if (dec_checkmarker(decode)) { printf("in %s line %d mx %d my %d\n", __FUNCTION__, __LINE__, mx, my); return ERR_WRONG_MARKER; } decode_mcus(&decode->in, dcts, 4, decode->dscans, max); if ((my >= cropy1) && (my < mcusy - cropy2) && (mx >= cropx1) && (mx < mcusx - cropx2)) { idct(dcts, out, decode->dquant[0], IFIX(128.5), max[0]); idct(dcts + 64, out + 64, decode->dquant[0], IFIX(128.5), max[1]); idct(dcts + 128, out + 256, decode->dquant[1], IFIX(0.5), max[2]); idct(dcts + 192, out + 320, decode->dquant[2], IFIX(0.5), max[3]); pic0 = pic + picx + picy; pic1 = pic0 + softwidth; outv = V + (pocx + pocy); outu = U + (pocx + pocy); outy = out; outy1 = 0; outy2 = 8; inv = out + 64 * 4; inu = out + 64 * 5; for (j = 0; j < 4; j++) { for (k = 0; k < 8; k++) { if (k == 4) { outy1 += 56; outy2 += 56; } /* outup 4 pixels */ *pic0++ = CLIP(outy[outy1]); outy1++; *pic0++ = CLIP(outy[outy1]); outy1++; *pic1++ = CLIP(outy[outy2]); outy2++; *pic1++ = CLIP(outy[outy2]); outy2++; /* maybe one day yuv422P */ *outv++ = CLIP(128 + *inv); inv++; *outu++ = CLIP(128 + *inu); inu++; } outy += 16; outy1 = 0; outy2 = 8; pic0 += nextline; pic1 += nextline; outv += nextuv; outu += nextuv; } picx += 16; pocx += 8; } } if (my >= cropy1) { picy += nextblk; pocy += nextnewline; } } } myframe->scanlength = (long) ((softwidth * softheight * 3) >> 1); break; default: break; } // end case return 0; } // y=0.656g+0.125b+0.226r #define RGB24_TO_Y(r,g,b) (CLIP(\ (((g) <<9)+((g)<<7)+((g)<<5)+((b)<<7)+((r)<<8)-((r)<<4)-((r)<<3))>>10)) // v=(r-y)0.656 #define YR_TO_V(r,y) (128 + \ (((((r)-(y)) << 9 )+(((r)-(y)) << 7 )+(((r)-(y)) << 5 )) >> 10)) // u=(b-y)0.5 #define YB_TO_U(b,y) (128 + \ (((b)-(y)) >> 1)) #define PACKRGB16(r,g,b) (__u16) ((((b) & 0xF8) << 8 ) | (((g) & 0xFC) << 3 ) | (((r) & 0xF8) >> 3 )) static int bayer_decode(struct spca50x_frame *myframe, int force_rgb) { int r_offset, g_offset, b_offset; int my, mx; /* scan input surface */ unsigned char *pic1; /* output surface */ __u16 *pix1, *pix2; /* same for 16 bits output */ unsigned char *U, *V; /* chroma output pointer */ unsigned char inr, ing1, ing2, inb, ing; /* srgb input */ int inl, inl1; /* offset line input */ int nextinline, nextoutline; unsigned char r, b, y1, y2, y3, y4; /*kernel matrix 4x4 */ unsigned char G00, R10, G20, R30, B01, G02, B03, G31, R32, G13, B23, G33; unsigned char r1, g1, b1, r2, g2, b2, r3, g3, b3, r4, g4, b4; int bpp; unsigned char *pic = myframe->data; /* output surface */ unsigned char *buf = myframe->tmpbuffer; /* input surface */ int width = myframe->hdrwidth; int height = myframe->hdrheight; int softwidth = myframe->width; int softheight = myframe->height; //int method = myframe->method; int format = myframe->format; int cropx1 = myframe->cropx1; int cropx2 = myframe->cropx2; int cropy1 = myframe->cropy1; int cropy2 = myframe->cropy2; unsigned char *red = myframe->decoder->Red; unsigned char *green = myframe->decoder->Green; unsigned char *blue = myframe->decoder->Blue; int framesize, frameUsize; inr = ing1 = ing2 = ing = inb = r = b = 0; //compiler maybe happy !! framesize = softwidth * softheight; frameUsize = framesize >> 2; /* rgb or bgr like U or V that's the question */ if (force_rgb) { V = pic + framesize; U = V + frameUsize; r_offset = 0; g_offset = 1; b_offset = 2; } else { U = pic + framesize; V = U + frameUsize; r_offset = 2; g_offset = 1; b_offset = 0; } /* initialize input pointer */ inl = 0; inl1 = width; nextinline = width << 1; /* Decode to the correct format. */ switch (format) { case VIDEO_PALETTE_RGB565: { bpp = 2; /* initialize */ pix1 = (__u16 *) pic; pix2 = pix1 + softwidth; for (my = 0; my < height; my += 2) { for (mx = 0; mx < width; mx += 2) { /* test if we need to decode */ if ((my >= cropy1) && (my < height - cropy2) && (mx >= cropx1) && (mx < width - cropx2)) { /* yes decode GBRG */ g1 = green[buf[inl + mx]]; b2 = blue[buf[inl + 1 + mx]]; r3 = red[buf[inl1 + mx]]; g4 = green[buf[inl1 + 1 + mx]]; if ((mx == 0) || (my == 0) || (mx == (width - 2)) || (my == (height - 2))) { ing = (g1 + g4) >> 1; if (force_rgb) { *pix1++ = PACKRGB16(r3, g1, b2); *pix1++ = PACKRGB16(r3, ing, b2); *pix2++ = PACKRGB16(r3, ing, b2); *pix2++ = PACKRGB16(r3, g4, b2); } else { *pix1++ = PACKRGB16(b2, g1, r3); *pix1++ = PACKRGB16(b2, ing, r3); *pix2++ = PACKRGB16(b2, ing, r3); *pix2++ = PACKRGB16(b2, g4, r3); } } else { G00 = buf[inl + mx - width - 1]; R10 = buf[inl + mx - width]; G20 = buf[inl + mx - width + 1]; R30 = buf[inl + mx - width + 2]; B01 = buf[inl + mx - 1]; G31 = buf[inl + mx + 2]; G02 = buf[inl1 + mx - 1]; R32 = buf[inl1 + mx + 2]; B03 = buf[inl1 + mx + width - 1]; G13 = buf[inl1 + mx + width]; B23 = buf[inl1 + mx + width + 1]; G33 = buf[inl1 + mx + width + 2]; b1 = blue[((B01 + b2) >> 1)]; r1 = red[((R10 + r3) >> 1)]; r4 = red[((r3 + R32) >> 1)]; b4 = blue[((b2 + B23) >> 1)]; g2 = green[((g1 + g4 + G31 + G20) >> 2)]; r2 = red[((R10 + R30 + r3 + R32) >> 2)]; g3 = green[((g1 + g4 + G13 + G02) >> 2)]; b3 = blue[((B01 + b2 + B23 + B03) >> 2)]; if (force_rgb) { *pix1++ = PACKRGB16(r1, g1, b1); *pix1++ = PACKRGB16(r2, g2, b2); *pix2++ = PACKRGB16(r3, g3, b3); *pix2++ = PACKRGB16(r4, g4, b4); } else { *pix1++ = PACKRGB16(b1, g1, r1); *pix1++ = PACKRGB16(b2, g2, r2); *pix2++ = PACKRGB16(b3, g3, r3); *pix2++ = PACKRGB16(b4, g4, r4); } } } // end test decode } // end mx loop inl += nextinline; inl1 += nextinline; if (my >= cropy1) { /* are we in a decode surface move the output pointer */ pix1 += (softwidth); pix2 += (softwidth); } } // end my loop } myframe->scanlength = (long) (softwidth * softheight * bpp); break; case VIDEO_PALETTE_RGB32: case VIDEO_PALETTE_RGB24: { bpp = (format == VIDEO_PALETTE_RGB32) ? 4 : 3; /* initialize */ nextoutline = bpp * softwidth; pic1 = pic + nextoutline; for (my = 0; my < height; my += 2) { for (mx = 0; mx < width; mx += 2) { /* test if we need to decode */ if ((my >= cropy1) && (my < height - cropy2) && (mx >= cropx1) && (mx < width - cropx2)) { /* yes decode GBRG */ g1 = green[buf[inl + mx]]; b2 = blue[buf[inl + 1 + mx]]; r3 = red[buf[inl1 + mx]]; g4 = green[buf[inl1 + 1 + mx]]; if ((mx == 0) || (my == 0) || (mx == (width - 2)) || (my == (height - 2))) { ing = (g1 + g4) >> 1; /* top pixel Right */ pic[r_offset] = r3; pic[g_offset] = g1; pic[b_offset] = b2; pic += bpp; /* top pixel Left */ pic[r_offset] = r3; pic[g_offset] = ing; pic[b_offset] = b2; pic += bpp; /* bottom pixel Right */ pic1[r_offset] = r3; pic1[g_offset] = ing; pic1[b_offset] = b2; pic1 += bpp; /* bottom pixel Left */ pic1[r_offset] = r3; pic1[g_offset] = g4; pic1[b_offset] = b2; pic1 += bpp; } else { G00 = buf[inl + mx - width - 1]; R10 = buf[inl + mx - width]; G20 = buf[inl + mx - width + 1]; R30 = buf[inl + mx - width + 2]; B01 = buf[inl + mx - 1]; G31 = buf[inl + mx + 2]; G02 = buf[inl1 + mx - 1]; R32 = buf[inl1 + mx + 2]; B03 = buf[inl1 + mx + width - 1]; G13 = buf[inl1 + mx + width]; B23 = buf[inl1 + mx + width + 1]; G33 = buf[inl1 + mx + width + 2]; b1 = blue[((B01 + b2) >> 1)]; r1 = red[((R10 + r3) >> 1)]; r4 = red[((r3 + R32) >> 1)]; b4 = blue[((b2 + B23) >> 1)]; g2 = green[((g1 + g4 + G31 + G20) >> 2)]; r2 = red[((R10 + R30 + r3 + R32) >> 2)]; g3 = green[((g1 + g4 + G13 + G02) >> 2)]; b3 = blue[((B01 + b2 + B23 + B03) >> 2)]; /* top pixel Right */ pic[r_offset] = r1; pic[g_offset] = g1; pic[b_offset] = b1; pic += bpp; /* top pixel Left */ pic[r_offset] = r2; pic[g_offset] = g2; pic[b_offset] = b2; pic += bpp; /* bottom pixel Right */ pic1[r_offset] = r3; pic1[g_offset] = g3; pic1[b_offset] = b3; pic1 += bpp; /* bottom pixel Left */ pic1[r_offset] = r4; pic1[g_offset] = g4; pic1[b_offset] = b4; pic1 += bpp; } } // end test decode } // end mx loop inl += nextinline; inl1 += nextinline; if (my >= cropy1) { /* are we in a decode surface move the output pointer */ pic += (nextoutline); pic1 += (nextoutline); } } // end my loop } myframe->scanlength = (long) (softwidth * softheight * bpp); break; case VIDEO_PALETTE_YUV420P: { /* Not yet implemented */ nextoutline = softwidth; pic1 = pic + nextoutline; for (my = 0; my < height; my += 2) { for (mx = 0; mx < width; mx += 2) { /* test if we need to decode */ if ((my >= cropy1) && (my < height - cropy2) && (mx >= cropx1) && (mx < width - cropx2)) { g1 = green[buf[inl + mx]]; b2 = blue[buf[inl + 1 + mx]]; r3 = red[buf[inl1 + mx]]; g4 = green[buf[inl1 + 1 + mx]]; if ((mx == 0) || (my == 0) || (mx == (width - 2)) || (my == (height - 2))) { ing = (g1 + g4) >> 1; /* top pixel Right */ y1 = RGB24_TO_Y(r3, g1, b2); *pic++ = y1; /* top pixel Left */ y2 = RGB24_TO_Y(r3, ing, b2); *pic++ = y2; /* bottom pixel Right */ y3 = RGB24_TO_Y(r3, ing, b2); *pic1++ = y3; /* bottom pixel Left */ y4 = RGB24_TO_Y(r3, g4, b2); *pic1++ = y4; /* U V plane */ *U++ = YB_TO_U(b2, ((y1 + y4) >> 1)); *V++ = YR_TO_V(r3, ((y1 + y4) >> 1)); } else { G00 = buf[inl + mx - width - 1]; R10 = buf[inl + mx - width]; G20 = buf[inl + mx - width + 1]; R30 = buf[inl + mx - width + 2]; B01 = buf[inl + mx - 1]; G31 = buf[inl + mx + 2]; G02 = buf[inl1 + mx - 1]; R32 = buf[inl1 + mx + 2]; B03 = buf[inl1 + mx + width - 1]; G13 = buf[inl1 + mx + width]; B23 = buf[inl1 + mx + width + 1]; G33 = buf[inl1 + mx + width + 2]; b1 = blue[((B01 + b2) >> 1)]; r1 = red[((R10 + r3) >> 1)]; r4 = red[((r3 + R32) >> 1)]; b4 = blue[((b2 + B23) >> 1)]; g2 = green[((g1 + g4 + G31 + G20) >> 2)]; r2 = red[((R10 + R30 + r3 + R32) >> 2)]; g3 = green[((g1 + g4 + G13 + G02) >> 2)]; b3 = blue[((B01 + b2 + B23 + B03) >> 2)]; /* top pixel Right */ y1 = RGB24_TO_Y(r1, g1, b1); *pic++ = y1; /* top pixel Left */ y2 = RGB24_TO_Y(r2, g2, b2); *pic++ = y2; /* bottom pixel Right */ y3 = RGB24_TO_Y(r3, g3, b3); *pic1++ = y3; /* bottom pixel Left */ y4 = RGB24_TO_Y(r4, g4, b4); *pic1++ = y4; /* U V plane */ *U++ = YB_TO_U(((b1 + b2 + b3 + b4) >> 2), ((y1 + y2 + y3 + y4) >> 2)); *V++ = YR_TO_V(((r1 + r2 + r3 + r4) >> 2), ((y1 + y2 + y3 + y4) >> 2)); } } // end test decode } // end mx loop inl += nextinline; inl1 += nextinline; if (my >= cropy1) { /* are we in a decode surface move the output pointer */ pic += softwidth; pic1 += softwidth; } } // end my loop } myframe->scanlength = (long) ((softwidth * softheight * 3) >> 1); break; default: break; } // end case return 0; } // end bayer_decode /* this function restore the missing header for the jpeg camera */ /* adapted from Till Adam create_jpeg_from_data() */ static int make_jpeg(struct spca50x_frame *myframe) { __u8 *start; int i; __u8 value; int width = myframe->hdrwidth; int height = myframe->hdrheight; long inputsize = myframe->scanlength; __u8 *buf = myframe->tmpbuffer; __u8 *dst = myframe->data; start = dst; /* set up the default header */ memcpy(dst, JPEGHeader, JPEGHEADER_LENGTH); /* setup quantization table */ *(dst + 6) = 0; memcpy(dst + 7, myframe->decoder->quant[0], 64); *(dst + 7 + 64) = 1; memcpy(dst + 8 + 64, myframe->decoder->quant[1], 64); *(dst + 564) = width & 0xFF; //Image width low byte *(dst + 563) = width >> 8 & 0xFF; //Image width high byte *(dst + 562) = height & 0xFF; //Image height low byte *(dst + 561) = height >> 8 & 0xFF; //Image height high byte /* set the format */ if (myframe->cameratype == JPEG) { *(dst + 567) = 0x22; dst += JPEGHEADER_LENGTH; for (i = 0; i < inputsize; i++) { value = *(buf + i) & 0xFF; *dst = value; dst++; if (value == 0xFF) { *dst = 0; dst++; } } } else { *(dst + 567) = 0x21; dst += JPEGHEADER_LENGTH; memcpy(dst, buf, inputsize); dst += inputsize; } /* Add end of image marker */ *(dst++) = 0xFF; *(dst++) = 0xD9; myframe->scanlength = (long) (dst - start); return 0; } static int make_jpeg_conexant(struct spca50x_frame *myframe) { __u8 *buf = myframe->data; __u8 *dst = myframe->tmpbuffer; memcpy(dst, JPEGHeader, JPEGHEADER_LENGTH - 33); *(dst + 6) = 0; memcpy(dst + 7, myframe->decoder->quant[0], 64); *(dst + 7 + 64) = 1; memcpy(dst + 8 + 64, myframe->decoder->quant[1], 64); dst += (JPEGHEADER_LENGTH - 33); memcpy(dst, buf, myframe->scanlength); myframe->scanlength += (JPEGHEADER_LENGTH - 33); return 0; } pwcbsd/spca5xx-20060402/drivers/usb/spcadecoder.h000755 000423 000000 00000000656 10553461761 022002 0ustar00luigiwheel000000 000000 #ifndef SPCADECODER_H #define SPCADECODER_H #include "spca5xx.h" /*********************************/ // int spca50x_outpicture(struct spca50x_frame *myframe); // void init_jpeg_decoder(struct usb_spca50x *spca50x); void init_sonix_decoder(struct usb_spca50x *spca50x); void init_pixart_decoder(struct usb_spca50x *spca50x); // void init_qTable(struct usb_spca50x *spca50x, unsigned int qIndex); #endif /* SPCADECODER_H */ pwcbsd/spca5xx-20060402/drivers/usb/spcagamma.h000744 000423 000000 00000023764 10546676143 021466 0ustar00luigiwheel000000 000000 #ifndef GAMMA_TABLES_H #define GAMMA_TABLES_H /* Gamma tables for the spca5xx camera (25/12/2003) by mxhaard@magic.fr * Original work from * Adam M. Costello * Gamma_tables_h is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Gamma_tables_h is distributed in the hope that it will be useful, * 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. * */ /* Gtable[0][n] -> 2.2 * Gtable[1][n] -> 1.7 * Gtable[2][n] -> 1.45 * Gtable[3][n] -> 1 * Gtable[4][n] -> 0.6896 * Gtable[5][n] -> 0.5882 * Gtable[6][n] -> 0.4545 * */ unsigned char GTable[7][256] = { // gamma-coeff = 2.2000 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 22, 22, 23, 23, 24, 25, 25, 26, 26, 27, 28, 28, 29, 30, 30, 31, 32, 33, 33, 34, 35, 35, 36, 37, 38, 39, 39, 40, 41, 42, 43, 43, 44, 45, 46, 47, 48, 49, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 73, 74, 75, 76, 77, 78, 79, 81, 82, 83, 84, 85, 87, 88, 89, 90, 91, 93, 94, 95, 97, 98, 99, 100, 102, 103, 105, 106, 107, 109, 110, 111, 113, 114, 116, 117, 119, 120, 121, 123, 124, 126, 127, 129, 130, 132, 133, 135, 137, 138, 140, 141, 143, 145, 146, 148, 149, 151, 153, 154, 156, 158, 159, 161, 163, 165, 166, 168, 170, 172, 173, 175, 177, 179, 181, 182, 184, 186, 188, 190, 192, 194, 196, 197, 199, 201, 203, 205, 207, 209, 211, 213, 215, 217, 219, 221, 223, 225, 227, 229, 231, 234, 236, 238, 240, 242, 244, 246, 248, 251, 253, 255, }, // gamma-coeff = 1.7000 { 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 9, 9, 10, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 17, 17, 18, 18, 19, 19, 20, 21, 21, 22, 22, 23, 24, 24, 25, 26, 26, 27, 28, 28, 29, 30, 30, 31, 32, 33, 33, 34, 35, 36, 36, 37, 38, 39, 39, 40, 41, 42, 43, 43, 44, 45, 46, 47, 48, 48, 49, 50, 51, 52, 53, 54, 55, 56, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 87, 88, 89, 90, 91, 92, 93, 94, 95, 97, 98, 99, 100, 101, 102, 103, 105, 106, 107, 108, 109, 111, 112, 113, 114, 115, 117, 118, 119, 120, 122, 123, 124, 125, 127, 128, 129, 131, 132, 133, 134, 136, 137, 138, 140, 141, 142, 144, 145, 146, 148, 149, 151, 152, 153, 155, 156, 157, 159, 160, 162, 163, 164, 166, 167, 169, 170, 172, 173, 175, 176, 177, 179, 180, 182, 183, 185, 186, 188, 189, 191, 192, 194, 195, 197, 198, 200, 202, 203, 205, 206, 208, 209, 211, 212, 214, 216, 217, 219, 220, 222, 224, 225, 227, 228, 230, 232, 233, 235, 237, 238, 240, 242, 243, 245, 247, 248, 250, 252, 253, 255, }, // gamma-coeff = 1.4500 { 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 5, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 13, 13, 14, 14, 15, 16, 16, 17, 17, 18, 19, 19, 20, 21, 21, 22, 23, 23, 24, 25, 25, 26, 27, 28, 28, 29, 30, 31, 31, 32, 33, 34, 34, 35, 36, 37, 38, 38, 39, 40, 41, 42, 42, 43, 44, 45, 46, 47, 47, 48, 49, 50, 51, 52, 53, 54, 55, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 114, 115, 116, 117, 118, 119, 120, 122, 123, 124, 125, 126, 127, 129, 130, 131, 132, 133, 134, 136, 137, 138, 139, 140, 142, 143, 144, 145, 147, 148, 149, 150, 151, 153, 154, 155, 156, 158, 159, 160, 161, 163, 164, 165, 166, 168, 169, 170, 172, 173, 174, 175, 177, 178, 179, 181, 182, 183, 185, 186, 187, 188, 190, 191, 192, 194, 195, 196, 198, 199, 200, 202, 203, 205, 206, 207, 209, 210, 211, 213, 214, 215, 217, 218, 220, 221, 222, 224, 225, 227, 228, 229, 231, 232, 234, 235, 236, 238, 239, 241, 242, 243, 245, 246, 248, 249, 251, 252, 254, 255, }, // gamma-coeff = 1.0000 { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, }, // gamma-coeff = 0.6896 { 0, 6, 9, 12, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 34, 36, 38, 39, 41, 43, 44, 46, 47, 49, 50, 51, 53, 54, 56, 57, 58, 60, 61, 62, 64, 65, 66, 67, 69, 70, 71, 72, 74, 75, 76, 77, 78, 79, 81, 82, 83, 84, 85, 86, 87, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 145, 146, 147, 148, 149, 150, 151, 152, 153, 153, 154, 155, 156, 157, 158, 159, 159, 160, 161, 162, 163, 164, 164, 165, 166, 167, 168, 169, 169, 170, 171, 172, 173, 174, 174, 175, 176, 177, 178, 178, 179, 180, 181, 182, 183, 183, 184, 185, 186, 187, 187, 188, 189, 190, 190, 191, 192, 193, 194, 194, 195, 196, 197, 197, 198, 199, 200, 201, 201, 202, 203, 204, 204, 205, 206, 207, 207, 208, 209, 210, 210, 211, 212, 213, 213, 214, 215, 216, 216, 217, 218, 219, 219, 220, 221, 222, 222, 223, 224, 225, 225, 226, 227, 227, 228, 229, 230, 230, 231, 232, 232, 233, 234, 235, 235, 236, 237, 237, 238, 239, 240, 240, 241, 242, 242, 243, 244, 245, 245, 246, 247, 247, 248, 249, 249, 250, 251, 252, 252, 253, 254, 254, 255, }, // gamma-coeff = 0.5882 { 0, 10, 15, 19, 22, 25, 28, 31, 33, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 55, 57, 59, 60, 62, 64, 65, 67, 68, 70, 71, 72, 74, 75, 77, 78, 79, 81, 82, 83, 84, 86, 87, 88, 89, 91, 92, 93, 94, 95, 97, 98, 99, 100, 101, 102, 103, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 144, 145, 146, 147, 148, 149, 150, 150, 151, 152, 153, 154, 155, 156, 156, 157, 158, 159, 160, 160, 161, 162, 163, 164, 164, 165, 166, 167, 168, 168, 169, 170, 171, 172, 172, 173, 174, 175, 175, 176, 177, 178, 178, 179, 180, 181, 181, 182, 183, 184, 184, 185, 186, 187, 187, 188, 189, 190, 190, 191, 192, 192, 193, 194, 195, 195, 196, 197, 197, 198, 199, 200, 200, 201, 202, 202, 203, 204, 204, 205, 206, 206, 207, 208, 208, 209, 210, 210, 211, 212, 212, 213, 214, 214, 215, 216, 216, 217, 218, 218, 219, 220, 220, 221, 222, 222, 223, 224, 224, 225, 226, 226, 227, 227, 228, 229, 229, 230, 231, 231, 232, 233, 233, 234, 234, 235, 236, 236, 237, 238, 238, 239, 239, 240, 241, 241, 242, 242, 243, 244, 244, 245, 245, 246, 247, 247, 248, 248, 249, 250, 250, 251, 251, 252, 253, 253, 254, 254, 255, }, // gamma-coeff = 0.4545 { 0, 21, 28, 34, 39, 43, 46, 50, 53, 56, 59, 61, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 85, 87, 89, 90, 92, 93, 95, 96, 98, 99, 101, 102, 103, 105, 106, 107, 109, 110, 111, 112, 114, 115, 116, 117, 118, 119, 120, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 144, 145, 146, 147, 148, 149, 150, 151, 151, 152, 153, 154, 155, 156, 156, 157, 158, 159, 160, 160, 161, 162, 163, 164, 164, 165, 166, 167, 167, 168, 169, 170, 170, 171, 172, 173, 173, 174, 175, 175, 176, 177, 178, 178, 179, 180, 180, 181, 182, 182, 183, 184, 184, 185, 186, 186, 187, 188, 188, 189, 190, 190, 191, 192, 192, 193, 194, 194, 195, 195, 196, 197, 197, 198, 199, 199, 200, 200, 201, 202, 202, 203, 203, 204, 205, 205, 206, 206, 207, 207, 208, 209, 209, 210, 210, 211, 212, 212, 213, 213, 214, 214, 215, 215, 216, 217, 217, 218, 218, 219, 219, 220, 220, 221, 221, 222, 223, 223, 224, 224, 225, 225, 226, 226, 227, 227, 228, 228, 229, 229, 230, 230, 231, 231, 232, 232, 233, 233, 234, 234, 235, 235, 236, 236, 237, 237, 238, 238, 239, 239, 240, 240, 241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 246, 246, 247, 247, 248, 248, 249, 249, 249, 250, 250, 251, 251, 252, 252, 253, 253, 254, 254, 255, 255, }, }; #endif /* GAMMA_TABLES_H */ pwcbsd/spca5xx-20060402/drivers/usb/spcausb.h000755 000423 000000 00000016016 10552530066 021155 0ustar00luigiwheel000000 000000 #ifndef SPCAUSB_H #define SPCAUSB_INIT_H #if defined(__FreeBSD__) #define TimeOut 1000 /* XXX 1 second ? */ #else #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 6) #define wait_ms(a) msleep((a)) #endif #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 11) #define TimeOut 1000 #else #define TimeOut HZ #endif #endif /* !__FreeBSD__ */ #if 0 static int spca_clear_feature(struct usb_device *dev, int endpoint) { int inpipe; inpipe = usb_rcvintpipe(dev,endpoint); usb_clear_halt(dev, inpipe); return 0; } #endif static int spca50x_setup_qtable(struct usb_spca50x *spca50x, unsigned int request, unsigned int ybase, unsigned int cbase, unsigned char qtable[2][64]); /* Alias setting */ /***************************** Implementation ****************************/ /* Read request from the device, using a vendor-specific request req */ /* XXX was spca5xxRegRead() */ static int usb_rd_vend_dev(struct usb_device *dev, __u16 req, __u16 value, __u16 index, __u8 * buffer, __u16 length) { return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), req, UT_READ_VENDOR_DEVICE, value, index, buffer, length, TimeOut); } int em28xx_read_reg_req(struct usb_device *dev, uint16_t req, uint16_t reg) { uint8_t buf; usb_rd_vend_dev(dev, req, 0x0000 /* val */, reg, &buf, 1); return buf; } int em28xx_read_reg(struct usb_device *dev, uint16_t reg) { return em28xx_read_reg_req(dev, UR_GET_STATUS, reg); } /* Write request to the device, using a vendor-specific request req */ /* was spca5xxRegWrite */ static int usb_wr_vend_dev(struct usb_device *dev, __u16 req, __u16 value, __u16 index, __u8 * buffer, __u16 length) { return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), req, UT_WRITE_VENDOR_DEVICE, value, index, buffer, length, TimeOut); } int em28xx_write_regs_req(struct usb_device *dev, uint16_t req, uint16_t reg, uint8_t *buf, uint16_t len) { usb_wr_vend_dev(dev, req, 0x0000 /* val */, reg, buf, len); return 0; } int em28xx_write_regs(struct usb_device *dev, uint16_t reg, uint8_t *buf, uint16_t len) { return em28xx_write_regs_req(dev, UR_GET_STATUS, reg, buf, len); } /* Read from the _interface_, vendor specific */ /* XXX was sonixRegRead */ static int usb_rd_vend_int(struct usb_device *dev, __u16 req, __u16 value, __u16 index, __u8 * buffer, __u16 length) { return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), req, UT_READ_VENDOR_INTERFACE, value, index, buffer, length, TimeOut); } #define Et_RegRead(dev,req,value,index,buffer,length) \ usb_rd_vend_int(dev,req,value,index,buffer,length) /* Write to the _interface_, vendor specific */ /* XXX was sonixRegWrite */ static int usb_wr_vend_int(struct usb_device *dev, __u16 req, __u16 value, __u16 index, __u8 * buffer, __u16 length) { return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), req, UT_WRITE_VENDOR_INTERFACE, value, index, buffer, length, TimeOut); } #define Et_RegWrite(dev,req,value,index,buffer,length) \ usb_wr_vend_int(dev,req,value,index,buffer,length) /* This is a standard request to set an alternate endpoint. * On freebsd should probably use usbd_set_interface(sc->sc_iface,sc->valternate); * XXX used only in one function. */ static int spca_set_interface(struct usb_device *dev, int interface, int alternate) { int ret; #if !defined(__FreeBSD__) struct usb_interface *iface; iface = usb_ifnum_to_if(dev, interface); if (!iface) { warn("selecting invalid interface %d", interface); return -EINVAL; } /* 9.4.10 says devices don't need this, if the interface only has one alternate setting */ if (iface->num_altsetting == 1) { dbg("ignoring set_interface for dev %d, iface %d, alt %d", dev->devnum, interface, alternate); return 0; } #endif /* !__FreeBSD__ */ if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), USB_REQ_SET_INTERFACE, USB_RECIP_INTERFACE /* UT_WRITE_INTERFACE */, alternate, interface, NULL, 0, TimeOut * 5)) < 0) return ret; return 0; } static int spca50x_reg_write(struct usb_device *dev, __u16 req, __u16 index, __u16 value) { int rc; rc = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), req, UT_WRITE_VENDOR_DEVICE, value, index, NULL, 0, TimeOut); PDEBUG(5, "reg write: 0x%02X,0x%02X:0x%02X, 0x%x", req, index, value, rc); if (rc < 0) err("reg write: error %d", rc); return rc; } /* Read a number of size 1-2-4 from given index, cmd req. */ static int spca50x_reg_read_with_value(struct usb_device *dev, __u16 req, __u16 value, __u16 index, __u16 length) { int i, rc; unsigned char buffer[4] = { 0, 0, 0, 0 }; /* Hope plp didn't ask for more */ if (length > sizeof(buffer)) { printf("Ouch, spca50x_reg_read_with_value: size %d too large\n", length); length = 4; } rc = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), req, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, value, index, buffer, length, TimeOut); i = UGETDW(buffer); PDEBUG(5, "reg read: 0x%02X,0x%02X:0x%04X error %d", req, index, i, rc); if (rc < 0) { err("reg read: error %d", rc); return rc; } return i; } /* * Read a number of size 1-2-4 from given index, cmd req. * returns: negative is error, pos or zero is data */ static int spca50x_reg_read(struct usb_device *dev, __u16 req, __u16 index, __u16 length) { return spca50x_reg_read_with_value(dev, req, 0, index, length); } /* * Simple function to wait for a given 8-bit value to be returned from * a spca50x_reg_read call. * Returns: negative is error or timeout, zero is success. */ static int spca50x_reg_readwait(struct usb_device *dev, __u16 reg, __u16 index, __u16 value) { int count = 0; int result = 0; while (count < 20) { result = spca50x_reg_read(dev, reg, index, 1); if (result == value) return 0; wait_ms(50); count++; } PDEBUG(2, "spca50x_reg_readwait failed"); return -EIO; } /* * Write a vector of 3 byte-entries (reg-value-index). * 0-0-0 is the terminator */ static int spca50x_write_vector(struct usb_spca50x *spca50x, __u16 data[][3]) { struct usb_device *dev = spca50x->dev; int err_code; int I = 0; while (data[I][0] != 0 || data[I][1] != 0 || data[I][2] != 0) { err_code = spca50x_reg_write(dev, data[I][0], data[I][2], data[I][1]); if (err_code < 0) { PDEBUG(1, "Register write failed for 0x%x,0x%x,0x%x", data[I][0], data[I][1], data[I][2]); return -1; } I++; } return 0; } static int spca50x_setup_qtable(struct usb_spca50x *spca50x, unsigned int request, unsigned int ybase, unsigned int cbase, unsigned char qtable[2][64]) { int i; int err; /* loop over y components */ for (i = 0; i < 64; i++) { err = spca50x_reg_write(spca50x->dev, request, ybase + i, qtable[0][i]); if (err < 0) { PDEBUG(2, "spca50x_reg_write failed"); return err; } } /* loop over c components */ for (i = 0; i < 64; i++) { err = spca50x_reg_write(spca50x->dev, request, cbase + i, qtable[1][i]); if (err < 0) { PDEBUG(2, "spca50x_reg_write failed"); return err; } } /* all ok */ return 0; } #endif /* SPCAUSB_H */ pwcbsd/spca5xx-20060402/drivers/usb/tas5130c.h000744 000423 000000 00000014536 10546676143 020775 0ustar00luigiwheel000000 000000 /**************************************************************************** # TAS5130C library # # Copyright (C) 2004 Michel Xhaard mxhaard@magic.fr # # # # This program is free software; you can redistribute it and/or modify # # it under the terms of the GNU General Public License as published by # # the Free Software Foundation; either version 2 of the License, or # # (at your option) any later version. # # # # This program is distributed in the hope that it will be useful, # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # # ****************************************************************************/ static __u16 tas5130cxx_start_data[][3] = { {0xa0, 0x01, 0x0000}, {0xa0, 0x50, 0x0002}, {0xa0, 0x03, 0x0008}, {0xa0, 0x02, 0x0010}, {0xa0, 0x01, 0x0001}, {0xa0, 0x00, 0x0001}, {0xa0, 0x01, 0x0012}, {0xa0, 0x01, 0x0001}, {0xa0, 0x05, 0x0012}, {0xa0, 0x07, 0x00a5}, {0xa0, 0x02, 0x00a6}, /********************/ {0xa0, 0x02, 0x0003}, {0xa0, 0x80, 0x0004}, {0xa0, 0x01, 0x0005}, {0xa0, 0xe0, 0x0006}, /********************/ {0xa0, 0x04, 0x0098}, {0xa0, 0x0f, 0x009a}, {0xa0, 0x04, 0x011a}, {0xa0, 0x0f, 0x011c}, {0xa0, 0xe8, 0x009c}, {0xa0, 0x02, 0x009d}, {0xa0, 0x88, 0x009e}, {0xa0, 0x06, 0x008d}, {0xa0, 0xf7, 0x0101}, {0xa0, 0x0d, 0x0100}, {0xa0, 0x06, 0x0189}, {0xa0, 0x68, 0x018d}, {0xa0, 0x60, 0x01a8}, {0xa0, 0x00, 0x01ad}, {0xa0, 0x03, 0x01c5}, {0xa0, 0x13, 0x01cb}, {0xa0, 0x08, 0x0250}, {0xa0, 0x08, 0x0301}, {0xa1, 0x01, 0x0002}, {0xa1, 0x01, 0x0008}, {0xa0, 0x03, 0x0008}, {0xa0, 0x08, 0x01c6}, {0xa1, 0x01, 0x01c8}, {0xa1, 0x01, 0x01c9}, {0xa1, 0x01, 0x01ca}, {0xa0, 0x0f, 0x01cb}, {0xa0, 0x68, 0x010a}, {0xa0, 0xec, 0x010b}, {0xa0, 0xec, 0x010c}, {0xa0, 0xec, 0x010d}, {0xa0, 0x68, 0x010e}, {0xa0, 0xec, 0x010f}, {0xa0, 0xec, 0x0110}, {0xa0, 0xec, 0x0111}, {0xa0, 0x68, 0x0112}, /****** MATRIXT *****/ {0xa1, 0x01, 0x018d}, {0xa0, 0x90, 0x018d}, //90 {0xa1, 0x01, 0x0180}, {0xa0, 0x00, 0x0180}, {0xa0, 0x00, 0x0019}, {0xa0, 0xa3, 0x0092}, {0xa0, 0x01, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0xa4, 0x0092}, {0xa0, 0x77, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x01, 0x00a3}, {0xa0, 0x77, 0x00a4}, {0xa0, 0x00, 0x0190}, //00 {0xa0, 0x03, 0x0191}, //03 {0xa0, 0xe8, 0x0192}, //e8 {0xa0, 0x0, 0x0195}, //0 {0xa0, 0x0, 0x0196}, //0 {0xa0, 0x7d, 0x0197}, //7d {0xa0, 0x0c, 0x018c}, {0xa0, 0x18, 0x018f}, {0xa0, 0x08, 0x01a9}, //08 {0xa0, 0x24, 0x01aa}, //24 {0xa0, 0xf0, 0x001d}, {0xa0, 0xf4, 0x001e}, {0xa0, 0xf8, 0x001f}, {0xa0, 0xff, 0x0020}, {0xa0, 0x03, 0x009f}, {0xa0, 0xc0, 0x00a0}, {0xa0, 0x50, 0x011d}, //50 {0xa0, 0x40, 0x0180}, {0xa1, 0x01, 0x0180}, {0xa0, 0x42, 0x0180}, /********************/ {0, 0, 0}, }; static __u16 tas5130cxx_scale_data[][3] = { {0xa0, 0x01, 0x0000}, {0xa0, 0x01, 0x0000}, {0xa0, 0x40, 0x0002}, /*******************/ {0xa0, 0x03, 0x0008}, {0xa1, 0x01, 0x0008}, /*******************/ {0xa0, 0x02, 0x0010}, {0xa0, 0x01, 0x0001}, {0xa0, 0x00, 0x0001}, {0xa0, 0x01, 0x0012}, {0xa0, 0x01, 0x0001}, {0xa0, 0x05, 0x0012}, {0xa0, 0x07, 0x00a5}, {0xa0, 0x02, 0x00a6}, {0xa0, 0x02, 0x0003}, {0xa0, 0x80, 0x0004}, {0xa0, 0x01, 0x0005}, {0xa0, 0xe0, 0x0006}, {0xa0, 0x05, 0x0098}, {0xa0, 0x0f, 0x009a}, {0xa0, 0x05, 0x011a}, {0xa0, 0x0f, 0x011c}, {0xa0, 0xe6, 0x009c}, {0xa0, 0x02, 0x009d}, {0xa0, 0x86, 0x009e}, {0xa0, 0x06, 0x008d}, {0xa0, 0x37, 0x0101}, {0xa0, 0x0d, 0x0100}, {0xa0, 0x06, 0x0189}, {0xa0, 0x68, 0x018d}, {0xa0, 0x60, 0x01a8}, {0xa0, 0x00, 0x01ad}, {0xa0, 0x03, 0x01c5}, {0xa0, 0x13, 0x01cb}, {0xa0, 0x08, 0x0250}, {0xa0, 0x08, 0x0301}, {0xa1, 0x01, 0x0002}, {0xa1, 0x01, 0x0008}, /*******************/ {0xa0, 0x03, 0x0008}, {0xa1, 0x01, 0x0008}, /*******************/ {0xa0, 0x08, 0x01c6}, {0xa1, 0x01, 0x01c8}, {0xa1, 0x01, 0x01c9}, {0xa1, 0x01, 0x01ca}, {0xa0, 0x0f, 0x01cb}, /*******************/ {0xa0, 0x68, 0x010a}, {0xa0, 0xec, 0x010b}, {0xa0, 0xec, 0x010c}, {0xa0, 0xec, 0x010d}, {0xa0, 0x68, 0x010e}, {0xa0, 0xec, 0x010f}, {0xa0, 0xec, 0x0110}, {0xa0, 0xec, 0x0111}, {0xa0, 0x68, 0x0112}, /*******************/ {0xa1, 0x01, 0x018d}, {0xa0, 0x90, 0x018d}, {0xa1, 0x01, 0x0180}, {0xa0, 0x00, 0x0180}, {0xa0, 0x00, 0x0019}, {0xa0, 0xa3, 0x0092}, {0xa0, 0x01, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0xa4, 0x0092}, {0xa0, 0x63, 0x0093}, {0xa0, 0x00, 0x0094}, {0xa0, 0x01, 0x0090}, {0xa1, 0x01, 0x0091}, {0xa0, 0x01, 0x00a3}, {0xa0, 0x63, 0x00a4}, {0xa0, 0x00, 0x0190}, {0xa0, 0x02, 0x0191}, {0xa0, 0x38, 0x0192}, {0xa0, 0x00, 0x0195}, {0xa0, 0x00, 0x0196}, {0xa0, 0x47, 0x0197}, {0xa0, 0x0c, 0x018c}, {0xa0, 0x18, 0x018f}, {0xa0, 0x08, 0x01a9}, {0xa0, 0x24, 0x01aa}, {0xa0, 0xd3, 0x001d}, {0xa0, 0xda, 0x001e}, {0xa0, 0xea, 0x001f}, {0xa0, 0xff, 0x0020}, {0xa0, 0x03, 0x009f}, {0xa0, 0x4c, 0x00a0}, {0xa0, 0x50, 0x011d}, {0xa0, 0x40, 0x0180}, {0xa1, 0x01, 0x0180}, {0xa0, 0x42, 0x0180}, /********************/ {0, 0, 0}, }; pwcbsd/spca5xx-20060402/drivers/usb/tv8532.h000755 000423 000000 00000037651 10552522127 020477 0ustar00luigiwheel000000 000000 /* * Quickcam cameras initialization data * */ /* Initialization data: this is the first set-up data written to the device (before the open data). */ #define TESTCLK 0x10 // reg 0x2c -> 0x12 //10 #define TESTCOMP 0x90 // reg 0x28 -> 0x80 #define TESTLINE 0x81 // reg 0x29 -> 0x81 #define QCIFLINE 0x41 // reg 0x29 -> 0x81 #define TESTPTL 0x14 // reg 0x2D -> 0x14 #define TESTPTH 0x01 // reg 0x2E -> 0x01 #define TESTPTBL 0x12 // reg 0x2F -> 0x0a #define TESTPTBH 0x01 // reg 0x30 -> 0x01 #define ADWIDTHL 0xe8 // reg 0x0c -> 0xe8 #define ADWIDTHH 0x03 // reg 0x0d -> 0x03 #define ADHEIGHL 0x90 // reg 0x0e -> 0x91 //93 #define ADHEIGHH 0x01 // reg 0x0f -> 0x01 #define EXPOL 0x8f // reg 0x1c -> 0x8f #define EXPOH 0x01 // reg 0x1d -> 0x01 #define ADCBEGINL 0x44 // reg 0x10 -> 0x46 //47 #define ADCBEGINH 0x00 // reg 0x11 -> 0x00 #define ADRBEGINL 0x0a // reg 0x14 -> 0x0b //0x0c #define ADRBEGINH 0x00 // reg 0x15 -> 0x00 #define TV8532_CMD_UPDATE 0x84 #define TV8532_EEprom_Add 0x03 #define TV8532_EEprom_DataL 0x04 #define TV8532_EEprom_DataM 0x05 #define TV8532_EEprom_DataH 0x06 #define TV8532_EEprom_TableLength 0x07 #define TV8532_EEprom_Write 0x08 #define TV8532_PART_CTRL 0x00 #define TV8532_CTRL 0x01 #define TV8532_CMD_EEprom_Open 0x30 #define TV8532_CMD_EEprom_Close 0x29 #define TV8532_UDP_UPDATE 0x31 #define TV8532_GPIO 0x39 #define TV8532_GPIO_OE 0x3B #define TV8532_REQ_RegWrite 0x02 #define TV8532_REQ_RegRead 0x03 #define TV8532_ADWIDTH_L 0x0C #define TV8532_ADWIDTH_H 0x0D #define TV8532_ADHEIGHT_L 0x0E #define TV8532_ADHEIGHT_H 0x0F #define TV8532_EXPOSURE 0x1C #define TV8532_QUANT_COMP 0x28 #define TV8532_MODE_PACKET 0x29 #define TV8532_SETCLK 0x2C #define TV8532_POINT_L 0x2D #define TV8532_POINT_H 0x2E #define TV8532_POINTB_L 0x2F #define TV8532_POINTB_H 0x30 #define TV8532_BUDGET_L 0x2A #define TV8532_BUDGET_H 0x2B #define TV8532_VID_L 0x34 #define TV8532_VID_H 0x35 #define TV8532_PID_L 0x36 #define TV8532_PID_H 0x37 #define TV8532_DeviceID 0x83 #define TV8532_AD_SLOPE 0x91 #define TV8532_AD_BITCTRL 0x94 #define TV8532_AD_COLBEGIN_L 0x10 #define TV8532_AD_COLBEGIN_H 0x11 #define TV8532_AD_ROWBEGIN_L 0x14 #define TV8532_AD_ROWBEGIN_H 0x15 /***************************************************************/ static void tv8532_initPictSetting(struct usb_spca50x *spca50x); static __u16 tv8532_getbrightness(struct usb_spca50x *spca50x); static __u16 tv8532_setbrightness(struct usb_spca50x *spca50x); static __u16 tv8532_setcontrast(struct usb_spca50x *spca50x); static void tv8532_configure(struct usb_spca50x *spca50x); static int tv8532_init(struct usb_spca50x *spca50x); static void tv8532_start(struct usb_spca50x *spca50x); static void tv8532_stop(struct usb_spca50x *spca50x); /****************************************************************/ static __u32 tv_8532_eeprom_data[] = { /*add dataL dataM dataH */ 0x00010001, 0x01018011, 0x02050014, 0x0305001c, 0x040d001e, 0x0505001f, 0x06050519, 0x0705011b, 0x0805091e, 0x090d892e, 0x0a05892f, 0x0b050dd9, 0x0c0509f1, 0 }; static void tv_8532WriteEEprom(struct usb_spca50x *spca50x) { int i = 0; __u8 reg, data0, data1, data2, datacmd; struct usb_device *dev = spca50x->dev; datacmd = 0xb0;; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_GPIO, &datacmd, 1); datacmd = TV8532_CMD_EEprom_Open; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_CTRL, &datacmd, 1); //wait_ms(1); while (tv_8532_eeprom_data[i]) { reg = (tv_8532_eeprom_data[i] & 0xFF000000) >> 24; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_EEprom_Add, ®, 1); //wait_ms(1); data0 = (tv_8532_eeprom_data[i] & 0x000000FF); usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_EEprom_DataL, &data0, 1); //wait_ms(1); data1 = (tv_8532_eeprom_data[i] & 0x0000FF00) >> 8; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_EEprom_DataM, &data1, 1); //wait_ms(1); data2 = (tv_8532_eeprom_data[i] & 0x00FF0000) >> 16; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_EEprom_DataH, &data2, 1); //wait_ms(1); datacmd = 0; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_EEprom_Write, &datacmd, 1); //wait_ms(10); i++; } datacmd = i; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_EEprom_TableLength, &datacmd, 1); //wait_ms(1); //udelay(1000); datacmd = TV8532_CMD_EEprom_Close; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_CTRL, &datacmd, 1); wait_ms(10); } static void tv8532_initPictSetting(struct usb_spca50x *spca50x) { /* set the initial value of brightness and contrast on probe */ spca50x->brightness = 0x018f << 7; spca50x->contrast = 0x80 << 8; } static __u16 tv8532_getbrightness(struct usb_spca50x *spca50x) { return spca50x->brightness; } static __u16 tv8532_setbrightness(struct usb_spca50x *spca50x) { __u8 value[2] = { 0xfc, 0x01 }; __u8 data; int brightness = (spca50x->brightness >> 7); if (brightness > 0x01FF) brightness = 0x01FF; if (brightness < 1) brightness = 1; value[1] = ((brightness >> 8) & 0xff); value[0] = ((brightness) & 0xff); usb_wr_vend_dev(spca50x->dev, TV8532_REQ_RegWrite, 0, TV8532_EXPOSURE, value, 2); //1c data = TV8532_CMD_UPDATE; usb_wr_vend_dev(spca50x->dev, TV8532_REQ_RegWrite, 0, TV8532_PART_CTRL, &data, 1); return 0; } static __u16 tv8532_setcontrast(struct usb_spca50x *spca50x) { return 0; } static void tv8532_configure(struct usb_spca50x *spca50x) { tv_8532WriteEEprom(spca50x); } static void tv_8532ReadRegisters(struct usb_spca50x *spca50x) { struct usb_device *dev = spca50x->dev; __u8 data = 0; //__u16 vid,pid; usb_rd_vend_dev(dev, TV8532_REQ_RegRead, 0, 0x0001, &data, 1); PDEBUG(1, "register 0x01-> %x", data); usb_rd_vend_dev(dev, TV8532_REQ_RegRead, 0, 0x0002, &data, 1); PDEBUG(1, "register 0x02-> %x", data); usb_rd_vend_dev(dev, TV8532_REQ_RegRead, 0, TV8532_ADWIDTH_L, &data, 1); usb_rd_vend_dev(dev, TV8532_REQ_RegRead, 0, TV8532_ADWIDTH_H, &data, 1); usb_rd_vend_dev(dev, TV8532_REQ_RegRead, 0, TV8532_QUANT_COMP, &data, 1); usb_rd_vend_dev(dev, TV8532_REQ_RegRead, 0, TV8532_MODE_PACKET, &data, 1); usb_rd_vend_dev(dev, TV8532_REQ_RegRead, 0, TV8532_SETCLK, &data, 1); usb_rd_vend_dev(dev, TV8532_REQ_RegRead, 0, TV8532_POINT_L, &data, 1); usb_rd_vend_dev(dev, TV8532_REQ_RegRead, 0, TV8532_POINT_H, &data, 1); usb_rd_vend_dev(dev, TV8532_REQ_RegRead, 0, TV8532_POINTB_L, &data, 1); usb_rd_vend_dev(dev, TV8532_REQ_RegRead, 0, TV8532_POINTB_H, &data, 1); usb_rd_vend_dev(dev, TV8532_REQ_RegRead, 0, TV8532_BUDGET_L, &data, 1); usb_rd_vend_dev(dev, TV8532_REQ_RegRead, 0, TV8532_BUDGET_H, &data, 1); usb_rd_vend_dev(dev, TV8532_REQ_RegRead, 0, TV8532_VID_L, &data, 1); usb_rd_vend_dev(dev, TV8532_REQ_RegRead, 0, TV8532_VID_H, &data, 1); usb_rd_vend_dev(dev, TV8532_REQ_RegRead, 0, TV8532_PID_L, &data, 1); usb_rd_vend_dev(dev, TV8532_REQ_RegRead, 0, TV8532_PID_H, &data, 1); usb_rd_vend_dev(dev, TV8532_REQ_RegRead, 0, TV8532_DeviceID, &data, 1); usb_rd_vend_dev(dev, TV8532_REQ_RegRead, 0, TV8532_AD_COLBEGIN_L, &data, 1); usb_rd_vend_dev(dev, TV8532_REQ_RegRead, 0, TV8532_AD_COLBEGIN_H, &data, 1); usb_rd_vend_dev(dev, TV8532_REQ_RegRead, 0, TV8532_AD_ROWBEGIN_L, &data, 1); usb_rd_vend_dev(dev, TV8532_REQ_RegRead, 0, TV8532_AD_ROWBEGIN_H, &data, 1); } static void tv_8532_setReg(struct usb_spca50x *spca50x) { struct usb_device *dev = spca50x->dev; __u8 data = 0; __u8 value[2] = { 0, 0 }; data = ADCBEGINL; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_AD_COLBEGIN_L, &data, 1); //0x10 data = ADCBEGINH; // also digital gain usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_AD_COLBEGIN_H, &data, 1); data = TV8532_CMD_UPDATE; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_PART_CTRL, &data, 1); //0x00<-0x84 data = 0x0a; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_GPIO_OE, &data, 1); /*******************************************************************/ data = ADHEIGHL; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_ADHEIGHT_L, &data, 1); //0e data = ADHEIGHH; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_ADHEIGHT_H, &data, 1); //0f value[0] = EXPOL; value[1] = EXPOH; // 350d 0x014c; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_EXPOSURE, value, 2); //1c data = ADCBEGINL; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_AD_COLBEGIN_L, &data, 1); //0x10 data = ADCBEGINH; // also digital gain usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_AD_COLBEGIN_H, &data, 1); data = ADRBEGINL; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_AD_ROWBEGIN_L, &data, 1); //0x14 data = 0x00; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_AD_SLOPE, &data, 1); //0x91 data = 0x02; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_AD_BITCTRL, &data, 1); //0x94 data = TV8532_CMD_EEprom_Close; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_CTRL, &data, 1); //0x01 data = 0x00; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_AD_SLOPE, &data, 1); //0x91 data = TV8532_CMD_UPDATE; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_PART_CTRL, &data, 1); //0x00<-0x84 } static void tv_8532_PollReg(struct usb_spca50x *spca50x) { struct usb_device *dev = spca50x->dev; __u8 data = 0; int i; /* strange polling from tgc */ for (i = 0; i < 10; i++) { data = TESTCLK; //0x48; //0x08; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_SETCLK, &data, 1); //0x2c data = TV8532_CMD_UPDATE; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_PART_CTRL, &data, 1); data = 0x01; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_UDP_UPDATE, &data, 1); //0x31 } } static int tv8532_init(struct usb_spca50x *spca50x) { struct usb_device *dev = spca50x->dev; __u8 data = 0; __u8 dataStart = 0; __u8 value[2] = { 0, 0 }; data = 0x32; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_AD_SLOPE, &data, 1); data = 0; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_AD_BITCTRL, &data, 1); tv_8532ReadRegisters(spca50x); data = 0x0b; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_GPIO_OE, &data, 1); value[0] = ADHEIGHL; value[1] = ADHEIGHH; // 401d 0x0169; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_ADHEIGHT_L, value, 2); //0e value[0] = EXPOL; value[1] = EXPOH; // 350d 0x014c; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_EXPOSURE, value, 2); //1c data = ADWIDTHL; // 0x20; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_ADWIDTH_L, &data, 1); //0x0c data = ADWIDTHH; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_ADWIDTH_H, &data, 1); // 0x0d /*******************************************************************/ data = TESTCOMP; //0x72 compressed mode usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_QUANT_COMP, &data, 1); //0x28 data = TESTLINE; //0x84; // CIF | 4 packet usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_MODE_PACKET, &data, 1); //0x29 /*******************************************************************/ data = TESTCLK; //0x48; //0x08; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_SETCLK, &data, 1); //0x2c data = TESTPTL; // 0x38; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_POINT_L, &data, 1); //0x2d data = TESTPTH; // 0x04; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_POINT_H, &data, 1); // 0x2e dataStart = TESTPTBL; //0x04; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_POINTB_L, &dataStart, 1); //0x2f data = TESTPTBH; //0x04; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_POINTB_H, &data, 1); //0x30 data = TV8532_CMD_UPDATE; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_PART_CTRL, &data, 1); //0x00<-0x84 /********************************************************************/ data = 0x01; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_UDP_UPDATE, &data, 1); //0x31 wait_ms(200); data = 0x00; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_UDP_UPDATE, &data, 1); //0x31 /********************************************************************/ tv_8532_setReg(spca50x); /*******************************************************************/ data = 0x0b; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_GPIO_OE, &data, 1); /*******************************************************************/ tv_8532_setReg(spca50x); /********************************************************************/ tv_8532_PollReg(spca50x); return 0; } static void tv8532_start(struct usb_spca50x *spca50x) { struct usb_device *dev = spca50x->dev; __u8 data = 0; __u8 dataStart = 0; __u8 value[2] = { 0, 0 }; __u16 err; data = 0x32; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_AD_SLOPE, &data, 1); data = 0; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_AD_BITCTRL, &data, 1); tv_8532ReadRegisters(spca50x); data = 0x0b; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_GPIO_OE, &data, 1); value[0] = ADHEIGHL; value[1] = ADHEIGHH; // 401d 0x0169; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_ADHEIGHT_L, value, 2); //0e tv8532_initPictSetting(spca50x); //value[0] = EXPOL; value[1] =EXPOH; // 350d 0x014c; //usb_wr_vend_dev(dev,TV8532_REQ_RegWrite,0,TV8532_EXPOSURE,value,2); //1c err = tv8532_setbrightness(spca50x); data = ADWIDTHL; // 0x20; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_ADWIDTH_L, &data, 1); //0x0c data = ADWIDTHH; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_ADWIDTH_H, &data, 1); // 0x0d /*******************************************************************/ data = TESTCOMP; //0x72 compressed mode usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_QUANT_COMP, &data, 1); //0x28 if (spca50x->mode) { data = QCIFLINE; //0x84; // CIF | 4 packet usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_MODE_PACKET, &data, 1); //0x29 } else { data = TESTLINE; //0x84; // CIF | 4 packet usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_MODE_PACKET, &data, 1); //0x29 } /*******************************************************************/ data = TESTCLK; //0x48; //0x08; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_SETCLK, &data, 1); //0x2c data = TESTPTL; // 0x38; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_POINT_L, &data, 1); //0x2d data = TESTPTH; // 0x04; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_POINT_H, &data, 1); // 0x2e dataStart = TESTPTBL; //0x04; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_POINTB_L, &dataStart, 1); //0x2f data = TESTPTBH; //0x04; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_POINTB_H, &data, 1); //0x30 data = TV8532_CMD_UPDATE; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_PART_CTRL, &data, 1); //0x00<-0x84 /********************************************************************/ data = 0x01; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_UDP_UPDATE, &data, 1); //0x31 wait_ms(200); data = 0x00; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_UDP_UPDATE, &data, 1); //0x31 /********************************************************************/ tv_8532_setReg(spca50x); /*******************************************************************/ data = 0x0b; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_GPIO_OE, &data, 1); /*******************************************************************/ tv_8532_setReg(spca50x); /********************************************************************/ tv_8532_PollReg(spca50x); data = 0x00; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_UDP_UPDATE, &data, 1); //0x31 } static void tv8532_stop(struct usb_spca50x *spca50x) { struct usb_device *dev = spca50x->dev; __u8 data = 0; data = 0x0b; usb_wr_vend_dev(dev, TV8532_REQ_RegWrite, 0, TV8532_GPIO_OE, &data, 1); } pwcbsd/spca5xx-20060402/drivers/usb/zc3xx.h000755 000423 000000 00000057415 10553461761 020612 0ustar00luigiwheel000000 000000 #ifndef ZC3XXUSB_H #define ZC3XXUSB_H /**************************************************************************** # Z-star zc301 zc302 P library # # Copyright (C) 2004 Michel Xhaard mxhaard@magic.fr # # # # This program is free software; you can redistribute it and/or modify # # it under the terms of the GNU General Public License as published by # # the Free Software Foundation; either version 2 of the License, or # # (at your option) any later version. # # # # This program is distributed in the hope that it will be useful, # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # # ****************************************************************************/ #include "tas5130c.h" #include "icm105a.h" #include "hdcs2020.h" #include "hv7131b.h" #include "pb0330.h" #include "hv7131c.h" #include "cs2102.h" #include "pas106b.h" #include "ov7630c.h" /******************* Camera Interface ***********************/ static __u16 zc3xx_getbrightness(struct usb_spca50x *spca50x); static __u16 zc3xx_getcontrast(struct usb_spca50x *spca50x); static void zc3xx_setbrightness(struct usb_spca50x *spca50x); static void zc3xx_setcontrast(struct usb_spca50x *spca50x); static int zc3xx_init(struct usb_spca50x *spca50x); static void zc3xx_start(struct usb_spca50x *spca50x); static void zc3xx_stop(struct usb_spca50x *spca50x); static int zc3xx_config(struct usb_spca50x *spca50x); static void zc3xx_shutdown(struct usb_spca50x *spca50x); static void zc3xx_setAutobright(struct usb_spca50x *spca50x); static void zc3xx_setquality(struct usb_spca50x *spca50x); /******************* Camera Private ***********************/ enum { SensorId = 0, reg8d, val8d, SensorReg1, valSreg1L, valSreg1H, SensorReg2, valSreg2L, valSreg2H, totval, }; #define VGATOT 9 static __u8 zcxxi2cSensor[VGATOT][totval] = { {0x00, 0xff, 0xff, 0x01, 0xaa, 0x00, 0xff, 0xff, 0xff}, // HV7131B {0x04, 0xff, 0xff, 0x01, 0xaa, 0x00, 0xff, 0xff, 0xff}, // CS2102 {0x06, 0x8d, 0x08, 0x11, 0xaa, 0x00, 0xff, 0xff, 0xff}, {0x08, 0xff, 0xff, 0x1c, 0x00, 0x00, 0x15, 0xaa, 0x00}, // HDCS2020 ? {0x0a, 0xff, 0xff, 0x07, 0xaa, 0xaa, 0xff, 0xff, 0xff}, // MI330 PB330 {0x0c, 0xff, 0xff, 0x01, 0xaa, 0x00, 0xff, 0xff, 0xff}, // ICM105 {0x0e, 0x8d, 0x08, 0x03, 0xaa, 0x00, 0xff, 0xff, 0xff}, // pas102 {0x02, 0xff, 0xff, 0x01, 0xaa, 0x00, 0xff, 0xff, 0xff}, {0x06, 0x8b, 0xa1, 0x12, 0x80, 0x00, 0x0a, 0x0b, 0xff}, // OV7630c }; #define SIFTOT 1 static __u8 zcxxi2cSensorSIF[SIFTOT][totval] = { #if 0 {0x01, 0xff, 0xff, 0x01, 0xaa, 0x00, 0xff, 0xff, 0xff}, // corrupt with 0x00 hv7131b reg 0 return 0x01 readonly {0x05, 0xff, 0xff, 0x01, 0xaa, 0x00, 0xff, 0xff, 0xff}, {0x07, 0x8d, 0x08, 0x11, 0xaa, 0x00, 0xff, 0xff, 0xff}, {0x09, 0xff, 0xff, 0x1c, 0x00, 0x00, 0x15, 0xaa, 0x00}, // corrupt with 0x08 hdcs2020 reg 0 return 0x18 readonly {0x0b, 0xff, 0xff, 0x07, 0xaa, 0xaa, 0xff, 0xff, 0xff}, {0x0d, 0xff, 0xff, 0x01, 0x11, 0x00, 0xff, 0xff, 0xff}, // corrupt with 0x0c ICM105 reg 0 is writable #endif {0x0f, 0x8d, 0x08, 0x03, 0xaa, 0x00, 0xff, 0xff, 0xff}, // PAS106 reg3 did not write with 0x0e !conflict PAS102 }; static __u8 zcxx3wrSensor[][5] = { {0x8b, 0xb3, 0x11, 0x12, 0xff}, // HV7131R {0x8b, 0x91, 0x14, 0x15, 0x16}, {0x8b, 0xe0, 0x14, 0x15, 0x16}, {0, 0, 0, 0, 0} }; static __u16 zcxx_i2cRead(struct usb_device *dev, __u8 reg) { __u8 retbyte = 0; __u8 retval[] = { 0, 0 }; usb_wr_vend_dev(dev, 0xa0, reg, 0x92, NULL, 0); usb_wr_vend_dev(dev, 0xa0, 0x02, 0x90, NULL, 0); // <-read command usb_rd_vend_dev(dev, 0xa1, 0x01, 0x0091, &retbyte, 1); // read status usb_rd_vend_dev(dev, 0xa1, 0x01, 0x0095, &retval[0], 1); // read Lowbyte usb_rd_vend_dev(dev, 0xa1, 0x01, 0x0096, &retval[1], 1); // read Hightbyte return ((retval[1] << 8) | retval[0]); } static __u8 zcxx_i2cWrite(struct usb_device *dev, __u8 reg, __u8 valL, __u8 valH) { __u8 retbyte = 0; usb_wr_vend_dev(dev, 0xa0, reg, 0x92, NULL, 0); usb_wr_vend_dev(dev, 0xa0, valL, 0x93, NULL, 0); usb_wr_vend_dev(dev, 0xa0, valH, 0x94, NULL, 0); usb_wr_vend_dev(dev, 0xa0, 0x01, 0x90, NULL, 0); // <-write command usb_rd_vend_dev(dev, 0xa1, 0x01, 0x0091, &retbyte, 1); // read status return (retbyte); } static int zcxx_probeSensor(struct usb_spca50x *spca50x) { int i, j; __u8 retbyte = 0; __u16 checkword = 0; __u16 checkid = 0xffff; /* check i2c */ /* check SIF */ for (i = 0; i < SIFTOT; i++) { usb_wr_vend_dev(spca50x->dev, 0xa0, 0x01, 0x0000, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0xa0, zcxxi2cSensorSIF[i][SensorId], 0x0010, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0xa0, 0x01, 0x0001, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0xa0, 0x03, 0x0012, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0xa0, 0x01, 0x0012, NULL, 0); //wait_ms(2); if (zcxxi2cSensorSIF[i][reg8d] == 0x8d) usb_wr_vend_dev(spca50x->dev, 0xa0, zcxxi2cSensorSIF[i][val8d], 0x008d, NULL, 0); wait_ms(150); retbyte = zcxx_i2cWrite(spca50x->dev, zcxxi2cSensorSIF[i][SensorReg1], zcxxi2cSensorSIF[i][valSreg1L], zcxxi2cSensorSIF[i][valSreg1H]); //wait_ms(2); retbyte = (zcxx_i2cRead(spca50x->dev, zcxxi2cSensorSIF[i][SensorReg1])) & 0xff; //wait_ms(2); PDEBUG(1, "sensor answer1 %d ", retbyte); if (retbyte != zcxxi2cSensorSIF[i][valSreg1L]) continue; if (retbyte == zcxxi2cSensorSIF[i][valSreg1L] && zcxxi2cSensorSIF[i][SensorReg2] == 0xff){ usb_wr_vend_dev(spca50x->dev, 0xa0, 0x01, 0x0000, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0xa0, 0x03, 0x003a, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0xa0, 0x0c, 0x003b, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0xa0, 0x08, 0x0038, NULL, 0); return zcxxi2cSensorSIF[i][SensorId]; } if (zcxxi2cSensorSIF[i][SensorReg2] != 0xff) { retbyte = zcxx_i2cWrite(spca50x->dev, zcxxi2cSensorSIF[i][SensorReg2], zcxxi2cSensorSIF[i][valSreg2L], zcxxi2cSensorSIF[i][valSreg2H]); retbyte = (zcxx_i2cRead (spca50x->dev, zcxxi2cSensorSIF[i][SensorReg2])) & 0xff; PDEBUG(1, "sensor answer2 %d ", retbyte); if (retbyte == zcxxi2cSensorSIF[i][valSreg2L]) return zcxxi2cSensorSIF[i][SensorId]; } } /* check VGA */ for (i = 0; i < VGATOT; i++) { if ((zcxxi2cSensor[i][reg8d] == 0x8b) && (zcxxi2cSensor[i][val8d] == 0xa1)) { /* code for stephane sensor ov7630c */ checkword = 0; usb_wr_vend_dev(spca50x->dev, 0xa0, 0x01, 0x0000, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0xa0, 0x01, 0x0001, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0xa0, zcxxi2cSensor[i][SensorId], 0x0010, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0xa0, zcxxi2cSensor[i][val8d], 0x008b, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0xa0, 0x08, 0x008d, NULL, 0); wait_ms(150); usb_wr_vend_dev(spca50x->dev, 0xa0, 0x01, 0x0012, NULL, 0); retbyte = zcxx_i2cWrite(spca50x->dev, zcxxi2cSensor[i][SensorReg1], zcxxi2cSensor[i][valSreg1L], zcxxi2cSensor[i][valSreg1H]); retbyte = (zcxx_i2cRead(spca50x->dev, zcxxi2cSensor[i][SensorReg2])) & 0xff; checkword = retbyte << 8; checkword |= ((zcxx_i2cRead(spca50x->dev, 0x0b)) & 0xff); if (checkword == 0x7631) { PDEBUG(0, "sensor answervga 0x%04X ", checkword); usb_wr_vend_dev(spca50x->dev, 0xa0, zcxxi2cSensor[i][SensorId], 0x0010, NULL, 0); return zcxxi2cSensor[i][SensorId]; } continue; } usb_wr_vend_dev(spca50x->dev, 0xa0, 0x01, 0x0000, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0xa0, zcxxi2cSensor[i][SensorId], 0x0010, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0xa0, 0x01, 0x0001, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0xa0, 0x03, 0x0012, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0xa0, 0x01, 0x0012, NULL, 0); wait_ms(2); if (zcxxi2cSensor[i][reg8d] == 0x8d) usb_wr_vend_dev(spca50x->dev, 0xa0, zcxxi2cSensor[i][val8d], 0x008d, NULL, 0); retbyte = zcxx_i2cWrite(spca50x->dev, zcxxi2cSensor[i][SensorReg1], zcxxi2cSensor[i][valSreg1L], zcxxi2cSensor[i][valSreg1H]); if (zcxxi2cSensor[i][SensorReg2] != 0xff) { retbyte = zcxx_i2cWrite(spca50x->dev, zcxxi2cSensor[i][SensorReg2], zcxxi2cSensor[i][valSreg2L], zcxxi2cSensor[i][valSreg2H]); retbyte = (zcxx_i2cRead(spca50x->dev, zcxxi2cSensor[i][SensorReg2])) & 0xff; } else { checkid = (zcxx_i2cRead(spca50x->dev,0x00)) & 0xff; PDEBUG(1, "check sensor id 0x%04X ", checkid); retbyte = (zcxx_i2cRead(spca50x->dev, zcxxi2cSensor[i][SensorReg1])) & 0xff; if(checkid == 2) // Who is here, that is a surprised :) continue ; } PDEBUG(1, "sensor answervga %d ", retbyte); if (retbyte != 0) return zcxxi2cSensor[i][SensorId]; } /* check 3 wires bus */ i = 0; while (zcxx3wrSensor[i][0]) { usb_wr_vend_dev(spca50x->dev, 0xa0, 0x02, 0x0010, NULL, 0); usb_rd_vend_dev(spca50x->dev, 0xa1, 0x01, 0x0010, &retbyte, 1); usb_wr_vend_dev(spca50x->dev, 0xa0, 0x01, 0x0000, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0xa0, 0x00, 0x0010, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0xa0, 0x01, 0x0001, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0xa0, zcxx3wrSensor[i][1], zcxx3wrSensor[i][0], NULL, 0); usb_wr_vend_dev(spca50x->dev, 0xa0, 0x03, 0x0012, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0xa0, 0x01, 0x0012, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0xa0, 0x05, 0x0012, NULL, 0); for (j = 2; j < 5; j++) { if (zcxx3wrSensor[i][j] != 0xff) { retbyte = (zcxx_i2cRead(spca50x->dev, zcxx3wrSensor[i][j])) & 0xff; if (retbyte != 0) return (i | 0x10); } } if (zcxx3wrSensor[i][1] == 0x91) { /* check Aurelien Jurgen sensor */ checkword = 0; usb_wr_vend_dev(spca50x->dev, 0xa0, 0x02, 0x0010, NULL, 0); usb_rd_vend_dev(spca50x->dev, 0xa1, 0x01, 0x000b, &retbyte, 1); checkword = retbyte << 8; usb_rd_vend_dev(spca50x->dev, 0xa1, 0x01, 0x000a, &retbyte, 1); checkword |= retbyte; PDEBUG(1, "sensor 3w Vga ??? 0x%04X ", (checkword & 0xffff)); if (checkword == 0xc001 || checkword == 0xe001) { spca50x->chip_revision = checkword; usb_rd_vend_dev(spca50x->dev, 0xa1, 0x01, 0x0010, &retbyte, 1); usb_wr_vend_dev(spca50x->dev, 0xa0, 0x01, 0x0000, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0xa0, 0x0d, 0x003a, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0xa0, 0x02, 0x003b, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0xa0, 0x00, 0x0038, NULL, 0); return 0x13; } } i++; } return (-1); } static __u16 zc3xxWriteVector(struct usb_spca50x *spca50x, __u16 data[][3]) { struct usb_device *dev = spca50x->dev; int err = 0; int i = 0; __u8 buffread; while (data[i][0]) { if (data[i][0] == 0xa0) { /* write registers */ usb_wr_vend_dev(dev, data[i][0], data[i][1], data[i][2], NULL, 0); } else { /* read status */ usb_rd_vend_dev(dev, data[i][0], data[i][1], data[i][2], &buffread, 1); } i++; udelay(1000); } return err; } #define CLAMP(x) (unsigned char)(((x)>0xFF)?0xff:(((x)<1)?1:(x))) static __u8 Tgamma[16] = { 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8, 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff }; static __u8 Tgradient[16] = { 0x26, 0x22, 0x20, 0x1c, 0x16, 0x13, 0x10, 0x0d, 0x0b, 0x09, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02 }; //static __u8 Tgamma[16]={0x24,0x44,0x64,0x84,0x9d,0xb2,0xc4,0xd3,0xe0,0xeb,0xf4,0xff,0xff,0xff,0xff,0xff}; //CS2102 //static __u8 Tgradient[16]={0x18,0x20,0x20,0x1c,0x16,0x13,0x10,0x0e,0x0b,0x09,0x07,0x00,0x00,0x00,0x00,0x01}; static __u16 zc3xx_getbrightness(struct usb_spca50x *spca50x) { spca50x->brightness = 0x80 << 8; spca50x->contrast = 0x80 << 8; return spca50x->brightness; } static __u16 zc3xx_getcontrast(struct usb_spca50x *spca50x) { return spca50x->contrast; } static void zc3xx_setbrightness(struct usb_spca50x *spca50x) { __u8 brightness; brightness = spca50x->brightness >> 8; usb_wr_vend_dev(spca50x->dev, 0xa0, brightness, 0x011d, NULL, 0); if (brightness < 0x70) { usb_wr_vend_dev(spca50x->dev, 0xa0, brightness + 0x10, 0x018d, NULL, 0); } else { usb_wr_vend_dev(spca50x->dev, 0xa0, 0x80, 0x018d, NULL, 0); } } static void zc3xx_setcontrast(struct usb_spca50x *spca50x) { __u16 contrast; int gm0 = 0; int gr0 = 0; int index = 0; int i; /* now get the index of gamma table */ contrast = zc3xx_getcontrast(spca50x); if ((index = contrast >> 13) > 6) index = 6; PDEBUG(2, "starting new table index %d ", index); for (i = 0; i < 16; i++) { gm0 = Tgamma[i] * index >> 2; gr0 = Tgradient[i] * index >> 2; usb_wr_vend_dev(spca50x->dev, 0xa0, CLAMP(gm0), 0x0120 + i, NULL, 0); //brightness usb_wr_vend_dev(spca50x->dev, 0xa0, CLAMP(gr0), 0x0130 + i, NULL, 0); // contrast //PDEBUG(0,"i %d gamma %d gradient %d",i ,Tgamma[i],Tgradient[i]); } } static int zc3xx_init(struct usb_spca50x *spca50x) { usb_wr_vend_dev(spca50x->dev, 0xa0, 0x01, 0x0000, NULL, 0); return 0; } static void set_zc3xxVGA(struct usb_spca50x *spca50x) { memset(spca50x->mode_cam, 0x00, TOTMODE * sizeof(struct mwebcam)); spca50x->mode_cam[VGA].width = 640; spca50x->mode_cam[VGA].height = 480; spca50x->mode_cam[VGA].t_palette = P_JPEG | P_RAW | P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[VGA].pipe = 1023; spca50x->mode_cam[VGA].method = 0; spca50x->mode_cam[VGA].mode = 0; spca50x->mode_cam[PAL].width = 384; spca50x->mode_cam[PAL].height = 288; spca50x->mode_cam[PAL].t_palette = P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[PAL].pipe = 1023; spca50x->mode_cam[PAL].method = 1; spca50x->mode_cam[PAL].mode = 0; spca50x->mode_cam[SIF].width = 352; spca50x->mode_cam[SIF].height = 288; spca50x->mode_cam[SIF].t_palette = P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[SIF].pipe = 1023; spca50x->mode_cam[SIF].method = 1; spca50x->mode_cam[SIF].mode = 0; spca50x->mode_cam[CIF].width = 320; spca50x->mode_cam[CIF].height = 240; spca50x->mode_cam[CIF].t_palette = P_JPEG | P_RAW | P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[CIF].pipe = 1023; spca50x->mode_cam[CIF].method = 0; spca50x->mode_cam[CIF].mode = 1; spca50x->mode_cam[QPAL].width = 192; spca50x->mode_cam[QPAL].height = 144; spca50x->mode_cam[QPAL].t_palette = P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[QPAL].pipe = 1023; spca50x->mode_cam[QPAL].method = 1; spca50x->mode_cam[QPAL].mode = 1; spca50x->mode_cam[QSIF].width = 176; spca50x->mode_cam[QSIF].height = 144; spca50x->mode_cam[QSIF].t_palette = P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[QSIF].pipe = 1023; spca50x->mode_cam[QSIF].method = 1; spca50x->mode_cam[QSIF].mode = 1; } static void set_zc3xxSIF(struct usb_spca50x *spca50x) { memset(spca50x->mode_cam, 0x00, TOTMODE * sizeof(struct mwebcam)); spca50x->mode_cam[SIF].width = 352; spca50x->mode_cam[SIF].height = 288; spca50x->mode_cam[SIF].t_palette = P_JPEG | P_RAW | P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[SIF].pipe = 1023; spca50x->mode_cam[SIF].method = 0; spca50x->mode_cam[SIF].mode = 0; spca50x->mode_cam[CIF].width = 320; spca50x->mode_cam[CIF].height = 240; spca50x->mode_cam[CIF].t_palette = P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[CIF].pipe = 1023; spca50x->mode_cam[CIF].method = 1; spca50x->mode_cam[CIF].mode = 0; spca50x->mode_cam[QPAL].width = 192; spca50x->mode_cam[QPAL].height = 144; spca50x->mode_cam[QPAL].t_palette = P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[QPAL].pipe = 1023; spca50x->mode_cam[QPAL].method = 1; spca50x->mode_cam[QPAL].mode = 0; spca50x->mode_cam[QSIF].width = 176; spca50x->mode_cam[QSIF].height = 144; spca50x->mode_cam[QSIF].t_palette = P_JPEG | P_RAW | P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[QSIF].pipe = 1023; spca50x->mode_cam[QSIF].method = 0; spca50x->mode_cam[QSIF].mode = 1; } static int zc3xx_config(struct usb_spca50x *spca50x) { int sensor = 0; __u8 bsensor = 0; spca50x->qindex = 1; sensor = zcxx_probeSensor(spca50x); switch (sensor) { case -1: PDEBUG(0, "Find Sensor UNKNOW_0 force Tas5130"); PWC_SC(spca50x)->pwc_info.sensor = SENSOR_TAS5130C; set_zc3xxVGA(spca50x); break; case 0: PDEBUG(0, "Find Sensor HV7131B"); PWC_SC(spca50x)->pwc_info.sensor = SENSOR_HV7131B; set_zc3xxVGA(spca50x); break; case 1: PDEBUG(0, "Find Sensor SIF UNKNOW_1"); break; case 0x02: PDEBUG(0, "Find Sensor UNKNOW_2"); break; case 0x04: PDEBUG(0, "Find Sensor CS2102"); PWC_SC(spca50x)->pwc_info.sensor = SENSOR_CS2102; set_zc3xxVGA(spca50x); break; case 5: PDEBUG(0, "Find Sensor SIF UNKNOW_5"); break; case 0x06: PDEBUG(0, "Find Sensor OV7630C"); PWC_SC(spca50x)->pwc_info.sensor = SENSOR_OV7630C; set_zc3xxVGA(spca50x); break; case 7: PDEBUG(0, "Find Sensor SIF UNKNOW_7"); break; case 0x08: PDEBUG(0, "Find Sensor HDCS2020(b)"); PWC_SC(spca50x)->pwc_info.sensor = SENSOR_HDCS2020b; set_zc3xxVGA(spca50x); break; case 9: PDEBUG(0, "Find Sensor SIF UNKNOW_9"); break; case 0x0a: PDEBUG(0, "Find Sensor PB0330"); PWC_SC(spca50x)->pwc_info.sensor = SENSOR_PB0330; set_zc3xxVGA(spca50x); break; case 0x0b: PDEBUG(0, "Find Sensor SIF UNKNOW_b"); break; case 0x0c: PDEBUG(0, "Find Sensor ICM105T"); PWC_SC(spca50x)->pwc_info.sensor = SENSOR_ICM105A; set_zc3xxVGA(spca50x); break; case 0x0d: PDEBUG(0, "Find Sensor SIF UNKNOW_d"); break; case 0x0e: PDEBUG(0, "Find Sensor PAS202BCB"); PWC_SC(spca50x)->pwc_info.sensor = SENSOR_HDCS2020; set_zc3xxVGA(spca50x); break; case 0x0f: PDEBUG(0, "Find Sensor PAS106"); PWC_SC(spca50x)->pwc_info.sensor = SENSOR_PAS106; set_zc3xxSIF(spca50x); break; case 0x10: PDEBUG(0, "Find Sensor TAS5130"); PWC_SC(spca50x)->pwc_info.sensor = SENSOR_TAS5130C; set_zc3xxVGA(spca50x); break; case 0x11: PDEBUG(0, "Find Sensor HV7131R(c)"); PWC_SC(spca50x)->pwc_info.sensor = SENSOR_HV7131C; set_zc3xxVGA(spca50x); break; case 0x12: PDEBUG(0, "Find Sensor TAS5130"); PWC_SC(spca50x)->pwc_info.sensor = SENSOR_TAS5130C; set_zc3xxVGA(spca50x); break; case 0x13: PDEBUG(0, "Find Sensor MI0360"); PWC_SC(spca50x)->pwc_info.sensor = SENSOR_PB0330; set_zc3xxVGA(spca50x); break; }; if ((sensor == 0x02) || (sensor == 0x01) || (sensor == 0x05) || (sensor == 0x07) || (sensor == 0x09) || (sensor == 0x0b) || (sensor == 0x0d)) { PDEBUG(0, "Our Sensor is unknow at the moment please report mxhaard@free.fr "); return -EINVAL; } if ((sensor == -1) || (sensor == 0x10) || (sensor == 0x12)) { usb_wr_vend_dev(spca50x->dev, 0xa0, 0x02, 0x0010, NULL, 0); usb_rd_vend_dev(spca50x->dev, 0xa1, 0x01, 0x0010, &bsensor, 1); } else { sensor = sensor & 0x0f; usb_wr_vend_dev(spca50x->dev, 0xa0, sensor, 0x0010, NULL, 0); usb_rd_vend_dev(spca50x->dev, 0xa1, 0x01, 0x0010, &bsensor, 1); } /* switch the led off */ if( sensor == 0x06) usb_wr_vend_dev(spca50x->dev,0xa0,0x01,0x0000,NULL,0); return 0; } static void zc3xx_setquality(struct usb_spca50x *spca50x) { __u8 quality = 0; quality = (spca50x->qindex) & 0xff; usb_wr_vend_dev(spca50x->dev, 0xa0, quality, 0x0008, NULL, 0); } static void zc3xx_setAutobright(struct usb_spca50x *spca50x) { __u8 autoval = 0; if (spca50x->autoexpo) autoval = 0x42; else autoval = 0x02; usb_wr_vend_dev(spca50x->dev, 0xa0, autoval, 0x0180, NULL, 0); } static void zc3xx_start(struct usb_spca50x *spca50x) { int err = 0; /* Assume start use the good resolution from spca50x->mode */ //err = zcxx_probeSensor(spca50x); switch (PWC_SC(spca50x)->pwc_info.sensor) { case SENSOR_TAS5130C: if (spca50x->mode) { /* 320x240 */ err = zc3xxWriteVector(spca50x, tas5130cxx_start_data); } else { /* 640x480 */ err = zc3xxWriteVector(spca50x, tas5130cxx_scale_data); } break; case SENSOR_ICM105A: if (spca50x->mode) { /* 320x240 */ err = zc3xxWriteVector(spca50x, icm105axx_start_data); } else { /* 640x480 */ err = zc3xxWriteVector(spca50x, icm105axx_scale_data); } break; case SENSOR_HDCS2020: if (spca50x->mode) { /* 320x240 */ err = zc3xxWriteVector(spca50x, hdcs2020xx_start_data); } else { /* 640x480 */ err = zc3xxWriteVector(spca50x, hdcs2020xx_scale_data); } break; case SENSOR_HDCS2020b: if (spca50x->mode) { /* 320x240 */ err = zc3xxWriteVector(spca50x, hdcs2020xb_start_data); } else { /* 640x480 */ err = zc3xxWriteVector(spca50x, hdcs2020xb_scale_data); } break; case SENSOR_HV7131B: err = zcxx_probeSensor(spca50x); if (spca50x->mode) { /* 320x240 */ err = zc3xxWriteVector(spca50x, hv7131bxx_start_data); } else { /* 640x480 */ err = zc3xxWriteVector(spca50x, hv7131bxx_scale_data); } break; case SENSOR_HV7131C: //HV7131R err = zcxx_probeSensor(spca50x); if (spca50x->mode) { /* 320x240 */ err = zc3xxWriteVector(spca50x, hv7131cxx_start_data); } else { /* 640x480 */ err = zc3xxWriteVector(spca50x, hv7131cxx_scale_data); } break; case SENSOR_PB0330: if (spca50x->mode) { /* 320x240 */ if ((spca50x->chip_revision == 0xc001) || (spca50x->chip_revision == 0xe001)) err = zc3xxWriteVector(spca50x, pb03303x_start_data); else err = zc3xxWriteVector(spca50x, pb0330xx_start_data); } else { /* 640x480 */ if ((spca50x->chip_revision == 0xc001) || (spca50x->chip_revision == 0xe001)) err = zc3xxWriteVector(spca50x, pb03303x_scale_data); else err = zc3xxWriteVector(spca50x, pb0330xx_scale_data); } break; case SENSOR_CS2102: if (spca50x->mode) { /* 320x240 */ err = zc3xxWriteVector(spca50x, cs2102_start_data); } else { /* 640x480 */ err = zc3xxWriteVector(spca50x, cs2102_scale_data); } break; case SENSOR_OV7630C: if (spca50x->mode) { /* 320x240 */ err = zc3xxWriteVector(spca50x, ov7630c_start_data); } else { /* 640x480 */ err = zc3xxWriteVector(spca50x, ov7630c_scale_data); } break; case SENSOR_PAS106: err= zc3xxWriteVector(spca50x, pas106b_com_data); if (spca50x->mode) { /* 176x144 */ err = zc3xxWriteVector(spca50x, pas106b_start_data); } else { /* 352x288 */ err = zc3xxWriteVector(spca50x, pas106b_scale_data); } break; } zc3xx_setbrightness(spca50x); zc3xx_setquality(spca50x); zc3xx_setAutobright(spca50x); } static void zc3xx_stop(struct usb_spca50x *spca50x) { // Nothing todo switch (PWC_SC(spca50x)->pwc_info.sensor) { case SENSOR_PAS106: usb_wr_vend_dev(spca50x->dev, 0xa0, 0x01, 0x0000, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0xa0, 0x03, 0x003a, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0xa0, 0x0c, 0x003b, NULL, 0); usb_wr_vend_dev(spca50x->dev, 0xa0, 0x08, 0x0038, NULL, 0); break; } } static void zc3xx_shutdown(struct usb_spca50x *spca50x) { struct usb_device *dev = spca50x->dev; __u8 buffread; usb_rd_vend_dev(dev, 0xa1, 0x01, 0x0180, &buffread, 1); usb_wr_vend_dev(dev, 0xa0, 0x00, 0x0180, NULL, 0); usb_wr_vend_dev(dev, 0xa0, 0x01, 0x0000, NULL, 0); } #endif // ZC3XXUSB_H pwcbsd/pwcbsd/Changelog000644 000423 000000 00000002552 10546676136 015714 0ustar00luigiwheel000000 000000 pwcbsd-1.3.1 --------------------------------------------------------------------- Driver: Changed SPC900NC to type 740 (fixes setting of led) Changed man page with examples for the new version of mplayer. pwcview: Restrict fps to valid range (5-30 fps) Fix build with the new version of SDL port pwcbsd-1.3 --------------------------------------------------------------------- Driver: Added support for the Samsung SNC-35E webcam. Reduced memory usage when mmap is disabled(default). Merged in some new ioctl's from the latest Linux pwc driver. Documented some more devicehints. Some general code cleanup. pwcview: By default uses a more efficient display videomode (lower CPU usage) Added commandline switches for tuning videomode. Display window is now resizable. Added ability to freeze/unfreeze the current frame. Added support for jpeg snapshots. Added option to take automatic jpeg snapshots in headless mode. Note that the command line options and arguments of pwcview have changed, consult the pwcview(1) manpage for details. pwcbsd-1.2 --------------------------------------------------------------------- Added support for mmap and the related v4l ioctl's. pwcbsd-1.1 --------------------------------------------------------------------- Implemented poll function, so applications can do poll and select on filedescriptor. Added support for Philips SPC900NC webcam. pwcbsd/pwcbsd/Makefile000644 000423 000000 00000001201 10546676136 015530 0ustar00luigiwheel000000 000000 all: ${MAKE} -f Makefile.kld all install: ${MAKE} -f Makefile.kld install clean: rm -f pwcview pwcsnap ${MAKE} -f Makefile.kld clean debug: echo '#define USB_DEBUG 1' > opt_usb.h ${MAKE} -f Makefile.kld all mmap: echo '#define USE_MMAP 1' > opt_usb.h ${MAKE} -f Makefile.kld all debugmmap: echo '#define USB_DEBUG 1' > opt_usb.h echo '#define USE_MMAP 1' >> opt_usb.h ${MAKE} -f Makefile.kld all pwcview: $(CC) -Wall -o pwcview -DLOCAL_VIDEODEV `/usr/local/bin/sdl-config --cflags --libs` -ljpeg pwcview.c pwcsnap: $(CC) -Wall -o pwcsnap -DNOGUI -DLOCAL_VIDEODEV -I/usr/local/include -L/usr/local/lib -ljpeg pwcview.c pwcbsd/pwcbsd/Makefile.kld000644 000423 000000 00000001511 10552522123 016264 0ustar00luigiwheel000000 000000 .PATH: ${.CURDIR} ${.CURDIR}/../spca5xx-20060402/drivers/usb \ ${.CURDIR}/../qce-ga-0.40d/ # add this to .PATH to import qce-ga # ${.CURDIR}/../qce-ga-0.40d/ SRCS=bus_if.h device_if.h opt_usb.h pwc.c pwc-ctrl.c \ pwc-addons.c pwc-info.c \ pwc-dec1.c pwc-dec23.c pwc-kiara.c pwc-misc.c \ pwc-timon.c pwc-uncompress.c pwc-v4l.c SRCS+= pwc-spca.c spca5xx.c spcadecoder.c KMOD=pwc KMODDIR?=/boot/modules #CFLAGS+= -I/usr/src/sys_new -DNEW_USB # for new driver CFLAGS+= -I../spca5xx-20060402/drivers/usb -DUSB_DEBUG CFLAGS += -DOLD_USB_COMPAT # qce-ga fixes #SRCS+= quickcam.c hdcs.c pb0100.c yuv.c vv6410.c # SRCS+= quickcam.c SRCS += hdcs.c pb0100.c vv6410.c CFLAGS+= -I../qce-ga-0.40d #DEBUG_FLAGS=-g spca5xx.o spcadecoder.o quickcam.o hdcs.o vv6410.o: ${CC} -c ${CFLAGS} -include linux_usbif.h $< .include pwcbsd/pwcbsd/README000644 000423 000000 00000004257 10546676136 014766 0ustar00luigiwheel000000 000000 PWCBSD - Philips USB webcam driver for FreeBSD 5.4 and higher ------------------------------------------------------------------------------ Q: Which webcams are supported? A: This driver should support all webcams that are supported by the original Linux pwc driver. Q: How to compile/install? A: Make sure you have kernel sources installed then either just type: make or if you want the driver to have mmap(*) support type: make mmap then as root type: make install (*) Some applications only work with the driver if it has mmap support. If you do enable mmap support in the driver beware that unplugging your webcam while it is in use may crash your system. So if you do enable mmap support always remember to first exit the application that is using the webcam before unplugging the webcam. Q: How to load driver? A: As root type: kldload pwc If the webcam was already plugged in before you loaded the driver you have to unplug it first.To load the driver at boot add: pwc_load="YES" to /boot/loader.conf. For more information about the driver and a list of supported webcams: nroff -man pwc.4|less pwcview ------------------------------------------------------------------------------ Q: What is it? A: Application to view video and alter settings of your webcam. Q: How to compile? A: Make sure you have the devel/sdl12 port and the graphics/jpeg port from the ports tree installed then just type: make pwcview For more information about the application: nroff -man pwcview.1|less pwcsnap ------------------------------------------------------------------------------ Q: What is it? A: A stripped down version of pwcview that can only run in headless mode and doesn't require the SDL libraries (Only linked to libc and libjpeg) Q: How to compile? A: Make sure you have the graphics/jpeg port from the ports tree installed then just type: make pwcsnap Other ------------------------------------------------------------------------------ multimedia/mplayer works out of the box with this driver. Other applications may need to be patched before they can work with this driver. For patches look on my website: http://raaf.atspace.org/ pwcbsd/pwcbsd/pwc-misc.c000644 000423 000000 00000033044 10552636735 015766 0ustar00luigiwheel000000 000000 /* * PWCBSD - Philips USB webcam driver for FreeBSD 5.4 and higher * * Copyright (C) 2006 Raaf * * Based on the Linux pwc driver. * * Copyright (C) 1999-2003 Nemosoft Unv. * Copyright (C) 2004-2006 Luc Saillard * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * 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 "pwc.h" /* used for the decoders - this is pwc-specific. */ #include "pwc-dec1.h" #include "pwc-dec23.h" struct pwc_format pwc_image_sizes[] = { { { 128, 96, 0 }, PSZ_SQCIF, "sqcif" }, { { 160, 120, 0 }, PSZ_QSIF, "qsif" }, { { 176, 144, 0 }, PSZ_QCIF, "qcif" }, { { 192, 144, 0 }, PSZ_QPAL, "qpal" }, { { 320, 240, 0 }, PSZ_SIF, "sif" }, { { 352, 288, 0 }, PSZ_CIF, "cif" }, { { 384, 288, 0 }, PSZ_PAL, "pal" }, { { 640, 480, 0 }, PSZ_VGA, "vga" }, { { 1024, 768, 0 }, PSZ_XGA, "xga" }, { { 0, 0, 0 }, 0, "none" }, }; /* x,y -> PSZ_ */ struct pwc_format *pwc_decode_size(struct pwc_softc *pdev, int width, int height) { int i; struct pwc_format *find = NULL; /* Make sure we don't go beyond our max size. NB: we have different limits for RAW and normal modes. In case you don't have the decompressor loaded or use RAW mode, the maximum viewable size is smaller. */ if (pdev->vpalette == VIDEO_PALETTE_RAW) { if (width > pdev->abs_max.x || height > pdev->abs_max.y) { Debug("VIDEO_PALETTE_RAW: going beyond abs_max.\n"); return NULL; } } else { if (width > pdev->view_max.x || height > pdev->view_max.y) { Debug("VIDEO_PALETTE_ not RAW: going beyond view_max.\n"); return NULL; } } /* Find the largest size supported by the camera that fits into the requested size. */ for (i = 0; pwc_image_sizes[i].xy.x != 0; i++) { struct pwc_coord *c = &pwc_image_sizes[i].xy; int id = pwc_image_sizes[i].id; if ( (pdev->image_mask & (1 << id)) == 0) continue; Trace(TRACE_SIZE, "pwc_decode_size i=%d wanted=%dx%d "\ " got=%dx%d\n", i, width, height, c->x, c->y); if(pdev->pwc_pad && c->x <= width && c->y <= height) { if (1 || find == NULL || find->xy.x * find->xy.y > c->x * c->y) find = &pwc_image_sizes[i]; } else if(c->x == width && c->y == height) { find = &pwc_image_sizes[i]; break; } } return find; } /* attach_cb for pwc */ static int pwc_construct(struct pwc_softc *sc) { int i; if(sc->pwc_info.devno.ud_vendor == 0x046D) { if(sc->pwc_info.devno.ud_product == 0x08B4) { sc->power_save = 1; } else if(sc->pwc_info.devno.ud_product == 0x08B5) { /* Logitech QuickCam Orbit */ sc->features |= FEATURE_MOTOR_PANTILT; sc->angle_range.pan_min = -7000; sc->angle_range.pan_max = 7000; sc->angle_range.tilt_min = -3000; sc->angle_range.tilt_max = 2500; } } if (pwc_get_cmos_sensor(sc, &i) >= 0) { int sensor; switch(i) { case 0x00: sensor = SENSOR_PWC_HYUNDAI; break; case 0x20: sensor = SENSOR_PWC_TDA8787; break; case 0x2E: sensor = SENSOR_PWC_SONY_Exas98L59; break; case 0x2F: sensor = SENSOR_PWC_SONY_ADI9804; break; case 0x30: sensor = SENSOR_PWC_SHARP_TDA8787; break; case 0x3E: sensor = SENSOR_PWC_SHARP_Exas98L59; break; case 0x3F: sensor = SENSOR_PWC_SHARP_ADI9804; break; case 0x40: sensor = SENSOR_PWC_UPA1021; break; case 0x100: sensor = SENSOR_PWC_VGA; break; case 0x101: sensor = SENSOR_PWC_PAL_MR; break; default: sensor = SENSOR_PWC_UNKNOWN; break; } sc->pwc_info.sensor = sensor; } switch(sc->pwc_info.type) { case 645: case 646: sc->view_min.x = 128; sc->view_min.y = 96; sc->view_max.x = 352; sc->view_max.y = 288; sc->abs_max.x = 352; sc->abs_max.y = 288; sc->image_mask = 1 << PSZ_SQCIF | 1 << PSZ_QCIF | 1 << PSZ_CIF; sc->vcinterface = 2; sc->vendpoint = 4; sc->frame_header_size = 0; sc->frame_trailer_size = 0; break; case 675: case 680: case 690: sc->view_min.x = 128; sc->view_min.y = 96; /* Anthill bug #38: PWC always reports max size, even without PWCX */ sc->view_max.x = 640; sc->view_max.y = 480; sc->image_mask = 1 << PSZ_SQCIF | 1 << PSZ_QSIF | 1 << PSZ_QCIF | 1 << PSZ_SIF | 1 << PSZ_CIF | 1 << PSZ_VGA; sc->abs_max.x = 640; sc->abs_max.y = 480; sc->vcinterface = 3; sc->vendpoint = 4; sc->frame_header_size = 0; sc->frame_trailer_size = 0; break; case 720: case 730: case 740: case 750: sc->view_min.x = 160; sc->view_min.y = 120; sc->view_max.x = 640; sc->view_max.y = 480; sc->image_mask = 1 << PSZ_QSIF | 1 << PSZ_SIF | 1 << PSZ_VGA; sc->abs_max.x = 640; sc->abs_max.y = 480; sc->vcinterface = 3; sc->vendpoint = 5; sc->frame_header_size = TOUCAM_HEADER_SIZE; sc->frame_trailer_size = TOUCAM_TRAILER_SIZE; break; } sc->vpalette = VIDEO_PALETTE_YUV420P; /* default */ sc->view_min.size = sc->view_min.x * sc->view_min.y; sc->view_max.size = sc->view_max.x * sc->view_max.y; /* length of image, in YUV format; always allocate enough memory. */ sc->len_per_image = (sc->abs_max.x * sc->abs_max.y * 3) / 2; pwc_set_leds(sc, 0, 0); if(sc->power_save) pwc_camera_power(sc, 0); return 0; } /* open callback for pwc * Do the malloc last so we don't have to undo it on failure. */ static int pwc_open_cb(struct pwc_softc *sc) { int err = 0; /* set fallback mode */ sc->vframes = 10; if(sc->pwc_info.type > 730) { sc->view.x = 160; sc->view.y = 120; } else { sc->view.x = 176; sc->view.y = 144; } /* Turn on camera */ if (sc->power_save) { err = pwc_camera_power(sc, 1); if (err < 0) { device_printf(sc->sc_dev, "Failed to restore power to the camera! (%d)\n", -err); return -err; } } pwc_set_leds(sc, sc->led_on, sc->led_off); /* Allocate decompressor table space */ if(sc->pwc_info.type == 645 || sc->pwc_info.type == 646) sc->decompress_data = malloc(sizeof(struct pwc_dec23_private), M_USBDEV, M_WAITOK);/* TOD O & FIXME */ else sc->decompress_data = malloc(sizeof(struct pwc_dec23_private), M_USBDEV, M_WAITOK);/* Tim on & Kiara */ if(sc->decompress_data == NULL) { device_printf(sc->sc_dev, "Failed to allocate decompress table.\n"); return ENOMEM; } return 0; } /* close routine for pwc */ static int pwc_close_cb(struct pwc_softc *sc) { if(sc->pwc_info.type == 645 || sc->pwc_info.type == 646) pwc_dec1_exit(); else pwc_dec23_exit(); /* Timon & Kiara */ set_alt_interface(sc->udev, sc->sc_iface, 0); pwc_set_leds(sc,0,0); if(sc->power_save) { if(pwc_camera_power(sc, 0) < 0) device_printf(sc->sc_dev, "Failed to power down the camera\n"); } return 0; } static int pwc_consume(struct pwc_softc *sc, unsigned char *iso_buf, int flen) { int awake = 0; /* need to awake the caller at the end */ struct pwc_frame_buf *fbuf = sc->fill_frame; unsigned char *fillptr = fbuf->data + fbuf->filled; /* XXX there is no individual framestatus in FreeBSD usbstack * so just assume all frames are good */ if (flen > 0) { /* if valid data... */ if (sc->vsync > VSYNC_NOCOPY) { /* ...and we are not sync-hunting... */ sc->vsync = VSYNC_SYNCHED; /* ...copy data to frame buffer, if possible */ if (flen + fbuf->filled > sc->frame_total_size) { Trace(TRACE_ISOC, "Frame buffer overflow (flen = %d,frame_total_size = %d).\n",flen, sc->frame_total_size); sc->vsync = VSYNC_NOCOPY; /* Hmm, let's wait for an EOF (end-of-frame) */ sc->vframes_error++; } else { memcpy(fillptr, iso_buf, flen); fillptr += flen; } } fbuf->filled += flen; } /* now find end of packet, if any */ if(flen >= sc->vlast_packet_size) goto eof_done; /* Shorter packet... We probably have the end of an image-frame; wake up read() process and let select()/poll() do something. Decompression is done in user time over there. */ if (sc->vsync != VSYNC_SYNCHED) goto start_new_frame; /* The ToUCam Fun CMOS sensor causes the firmware to send 2 or 3 bogus frames on the USB wire after an exposure change. This conditition is however detected in the cam and a bit is set in the header. */ if (sc->pwc_info.type == 730) { unsigned char *ptr = (unsigned char *)fbuf->data; if (ptr[1] == 1 && ptr[0] & 0x10) { sc->drop_frames += 2; sc->vframes_error++; } if ((ptr[0] ^ sc->vmirror) & 0x01) { if (ptr[0] & 0x01) { sc->snapshot_button_status = 1; Debug("pwc: Snapshot button pressed.\n"); } else { Debug("pwc: Snapshot button released.\n"); } } if ((ptr[0] ^ sc->vmirror) & 0x02) { if (ptr[0] & 0x02) { Debug("pwc: Image is mirrored.\n"); } else { Debug("pwc: Image is normal.\n"); } } sc->vmirror = ptr[0] & 0x03; /* Sometimes the trailer of the 730 is still sent as a 4 byte packet after a short frame; this condition is filtered out specifically. A 4 byte frame doesn't make sense anyway. So we get either this sequence: drop_bit set -> 4 byte frame -> short frame -> good frame Or this one: drop_bit set -> short frame -> good frame So we drop either 3 or 2 frames in all! */ if (fbuf->filled == 4) sc->drop_frames++; } else if (sc->pwc_info.type == 740 || sc->pwc_info.type == 720) { unsigned char *ptr = (unsigned char *)fbuf->data; if ((ptr[0] ^ sc->vmirror) & 0x01) { if (ptr[0] & 0x01) { sc->snapshot_button_status = 1; Debug("pwc: Snapshot button pressed.\n"); } else { Debug("pwc: Snapshot button released.\n"); } } sc->vmirror = ptr[0] & 0x03; } /* In case we were instructed to drop the frame, do so silently. The buffer pointers are not updated either (but the counters are reset below). */ if(sc->drop_frames > 0) { sc->drop_frames--; } /* Check for underflow first */ else if(sc->pwc_info.type != 0 && fbuf->filled < sc->frame_total_size) { Trace(TRACE_ISOC,"Frame buffer underflow (have %d bytes need %d); discarded.\n", fbuf->filled, sc->frame_total_size); sc->vframes_error++; } else { /* Send only once per EOF */ awake = 1; /* delay wake_ups */ /* Find our next frame to fill. This will always succeed, since we * nick a frame from either empty or full list, but if we had to * take it from the full list, it means a frame got dropped. */ pwc_next_fill_frame(sc); fbuf = sc->fill_frame; } sc->vframe_count++; start_new_frame: fbuf->filled = 0; fillptr = fbuf->data; sc->vsync = VSYNC_SYNCHUNT; eof_done: sc->vlast_packet_size = flen; return awake; } struct camera_callbacks pwc_callbacks = { .cb_attach = pwc_construct, .cb_open = pwc_open_cb, .cb_close = pwc_close_cb, .cb_consume = pwc_consume, .cb_decompress = pwc_decompress, }; /* * This is only for debugging. * Show the full configuration. If alt/ep are presented, select those * and do not print anything. */ usb_endpoint_descriptor_t * pwc_show_configs(struct pwc_softc *sc, int alt, int ep) { int i, l, ialt = (alt < 0 ? 0 : alt); usb_endpoint_descriptor_t *found = NULL, *edesc = NULL; if(sc->sc_iface == NULL || sc->sc_iface->idesc == NULL) { device_printf(sc->sc_dev, "Failed to get endpoint count\n"); return NULL; } printf("have %d configs\n", sc->udev->ddesc.bNumConfigurations); for (;;) { if (set_alt_interface(sc->udev, sc->sc_iface, ialt) != USBD_NORMAL_COMPLETION) { if (alt >= 0) printf("Set packet size: set alt config %d error\n", ialt); break; } for (i = 0; i < sc->sc_iface->idesc->bNumEndpoints; i++) { edesc = usbd_interface2endpoint_descriptor(sc->sc_iface, i); if (!edesc) continue; l = UGETW(edesc->wMaxPacketSize); printf("alt %d [%d] endp %d size %d:%4d attr 0x%x interval %d\n", ialt, i, UE_GET_ADDR(edesc->bEndpointAddress), (l >> 11) & 3, l & 0x7ff, edesc->bmAttributes, edesc->bInterval); if(UE_GET_ADDR(edesc->bEndpointAddress) == ep) found = edesc; } if (alt >= 0) break; ialt++; } return found; } pwcbsd/pwcbsd/pwc.c000644 000423 000000 00000074054 10553463415 015035 0ustar00luigiwheel000000 000000 /* * PWCBSD - Philips USB webcam driver for FreeBSD 5.4 and higher * * Copyright (C) 2006 Raaf * * Based on the Linux pwc driver. * * Copyright (C) 1999-2003 Nemosoft Unv. * Copyright (C) 2004-2006 Luc Saillard * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * 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 "pwc.h" static void pwc_isoc_handler(usbd_xfer_handle xfer, usbd_private_handle addr,usbd_status status); static void pwc_reset_buffers(struct pwc_softc *sc); static void pwc_free_buffers(struct pwc_softc *sc, int detach); #ifdef USB_DEBUG int pwcdebug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, pwc, CTLFLAG_RW, 0, "USB pwc"); SYSCTL_INT(_hw_usb_pwc, OID_AUTO, debug, CTLFLAG_RW,&pwcdebug, 0, "pwc debug level"); #endif #define MAX_ISOC_ERRORS 20 static d_open_t pwc_open; static d_close_t pwc_close; static d_read_t pwc_read; static d_ioctl_t pwc_ioctl; static d_mmap_t pwc_mmap; static d_poll_t pwc_poll; struct cdevsw pwc_cdevsw = { .d_version = D_VERSION, .d_flags = D_NEEDGIANT, .d_open = pwc_open, .d_close = pwc_close, .d_read = pwc_read, .d_ioctl = pwc_ioctl, .d_poll = pwc_poll, .d_mmap = pwc_mmap, .d_name = "pwc", }; static const struct pwc_info pwc_devs[] = { {{ 0x0471, 0x0302 }, 645, "Philips PCA645VC", &pwc_callbacks }, {{ 0x0471, 0x0303 }, 646, "Philips PCA646VC" }, {{ 0x0471, 0x0304 }, 646, "Askey VC010 type 2" }, {{ 0x0471, 0x0307 }, 675, "Philips PCVC675K (Vesta)" }, {{ 0x0471, 0x0308 }, 680, "Philips PCVC680K (Vesta Pro)" }, {{ 0x0471, 0x030C }, 690, "Philips PCVC690K (Vesta Pro Scan)" }, {{ 0x0471, 0x0310 }, 730, "Philips PCVC730K (ToUCam Fun)/PCVC830 (ToUCam II)" }, {{ 0x0471, 0x0311 }, 740, "Philips PCVC740K (ToUCam Pro)/PCVC840 (ToUCam II)" }, {{ 0x0471, 0x0312 }, 750, "Philips PCVC750K (ToUCam Pro Scan)" }, {{ 0x0471, 0x0313 }, 720, "Philips PCVC720K/40 (ToUCam XS)" }, {{ 0x0471, 0x0329 }, 740, "Philips SPC900NC" }, {{ 0x069A, 0x0001 }, 645, "Askey VC010 type 1" }, {{ 0x046D, 0x08B0 }, 740, "Logitech QuickCam Pro 3000" }, {{ 0x046D, 0x08B1 }, 740, "Logitech QuickCam Notebook Pro" }, {{ 0x046D, 0x08B2 }, 740, "Logitech QuickCam Pro 4000" }, {{ 0x046D, 0x08B3 }, 740, "Logitech QuickCam Zoom" }, {{ 0x046D, 0x08B4 }, 740, "Logitech QuickCam Zoom (new model)" }, {{ 0x046D, 0x08B5 }, 740, "Logitech QuickCam Orbit/Sphere" }, {{ 0x046D, 0x08B6 }, 730, "Logitech QuickCam (reserved ID)" }, {{ 0x046D, 0x08B7 }, 730, "Logitech QuickCam (reserved ID)" }, {{ 0x046D, 0x08B8 }, 730, "Logitech QuickCam (reserved ID)" }, {{ 0x055D, 0x9000 }, 675, "Samsung MPC-C10" }, {{ 0x055D, 0x9001 }, 675, "Samsung MPC-C30" }, {{ 0x055D, 0x9002 }, 740, "Samsung SNC-35E" }, {{ 0x041E, 0x400C }, 730, "Creative Labs Webcam 5" }, {{ 0x041E, 0x4011 }, 740, "Creative Labs Webcam Pro Ex" }, {{ 0x04CC, 0x8116 }, 730, "Sotec Afina Eye" }, {{ 0x06BE, 0x8116 }, 750, "AME Co. Afina Eye" }, {{ 0x0d81, 0x1910 }, 740, "Visionite VCS-UC300" }, {{ 0x0d81, 0x1900 }, 730, "Visionite VCS-UM100" }, }; #define pwc_lookup(v, p) ((const struct pwc_info *)usb_lookup(pwc_devs, v, p)) #define PWCUNIT(n) (minor(n)) USB_DECLARE_DRIVER(pwc); static const struct pwc_info * pwc_lookup1(int vendor, int product, struct pwc_softc *sc) { const struct pwc_info *p; p = pwc_lookup(vendor, product); if (p != NULL) { bcopy(p, &sc->pwc_info, sizeof(struct pwc_info)); if (sc->pwc_info.cb != NULL) bcopy(&sc->camera_cb, sc->pwc_info.cb, sizeof(struct camera_callbacks)); else { /* XXX should be the default */ sc->camera_cb = pwc_callbacks; } } else if (spcaDetectCamera(vendor, product, sc) == 0) { /* the descriptor is within the sc */ p = &sc->pwc_info; } return p; } /* * Try to claim the device. Remember, the softc might be discarded * after the match routine so we cannot trust it to store anything. * However, it is a convenient argument for some of the other routines * e.g. those used in the attach routine - see DEVICE_PROBE(9) * and device_get_softc(9) */ static int pwc_match(device_t dev) { struct pwc_softc *sc = device_get_softc(dev); struct usb_attach_arg *uaa = device_get_ivars(dev); usb_interface_descriptor_t *id; Trace(TRACE_PROBE,"pwc_match: vendor=0x%x, product=0x%x release=%04x\n",uaa->vendor, uaa->product,uaa->release); if(pwc_lookup1(uaa->vendor, uaa->product, sc) == NULL) return UMATCH_NONE; /* Driver loaded when device was already plugged in, we have to claim all interfaces or get none... */ if(uaa->usegeneric) return UMATCH_VENDOR_PRODUCT; /* We don't want all interfaces */ if(uaa->iface == NULL) return UMATCH_NONE; id = usbd_get_interface_descriptor(uaa->iface); if(id == NULL) { printf("pwc: failed to get interface descriptor\n"); return UMATCH_NONE; } Trace(TRACE_PROBE,"pwc_match: iface=%d\n",id->bInterfaceNumber); /* Interface 0 is the video interface * Interface 1 is supposed to be audiocontrol * Interface 2 is supposed to be audio */ printf("vend 0x%04x prod 0x%04x interface %d alt %d endpoints %d\n", uaa->vendor, uaa->product, id->bInterfaceNumber, id->bAlternateSetting, id->bNumEndpoints); if(id->bInterfaceNumber != 0) return UMATCH_NONE; return UMATCH_VENDOR_PRODUCT; } static int empty_consume(void) { return 0; } static int pwc_attach(device_t dev) { struct pwc_softc *sc = device_get_softc(dev); struct usb_attach_arg *uaa = device_get_ivars(dev); usb_device_descriptor_t *dd; char devinfo[1024]; const char *tmpstr; const struct pwc_info *info; int i, err; int unit = device_get_unit(dev); /* set up udev early so the lookup/attach routine can do more */ sc->udev = uaa->device; sc->sc_dev = dev; sc->release = uaa->release; info = pwc_lookup1(uaa->vendor, uaa->product, sc); if(info == NULL) { device_printf(sc->sc_dev, "attach error vendor/product mismatch (vendor=0x%x product=0x%x)\n", uaa->vendor,uaa->product); return ENXIO; } usbd_devinfo(uaa->device, 0, devinfo); device_set_desc_copy(dev, devinfo); device_printf(dev, "%s\n", devinfo); // sc->sc_iface = &dev->ifaces[0]; err = usbd_device2interface_handle(uaa->device,0,&sc->sc_iface); if(err) { device_printf(sc->sc_dev, "failed to get interface handle\n"); return ENXIO; } sc->power_save = 0; sc->spca50x.dev = sc->udev; sc->spca50x.pwc_softc = sc; /* XXX check callbacks ? */ if (sc->camera_cb.cb_consume == NULL) { printf("*** Warning, no data consumer callback\n"); sc->camera_cb.cb_consume = (consume_cb_t *)empty_consume; } if((dd = usbd_get_device_descriptor(sc->udev)) != NULL) { #if __FreeBSD_version >= 600034 char prod[USB_MAX_STRING_LEN]; char manufact[USB_MAX_STRING_LEN]; usbd_get_string(sc->udev,dd->iSerialNumber,sc->serial); usbd_get_string(sc->udev,dd->iProduct,prod); usbd_get_string(sc->udev,dd->iManufacturer,manufact); printf("prod <%s> manufacturer <%s> 0x%x 0x%x bcdev 0x%x\n", prod, manufact, UGETW(dd->idVendor), UGETW(dd->idProduct), UGETW(dd->bcdDevice)); #else strcpy(sc->serial,"serial only available under FreeBSD 6.0 and higher"); #endif } else { strcpy(sc->serial,"serial not found"); } mtx_init(&sc->ptrlock,device_get_name(sc->sc_dev),NULL,MTX_DEF); tmpstr = "video"; resource_string_value("pwc", unit, "devname", &tmpstr); sc->sc_dev_t = make_dev(&pwc_cdevsw, unit, UID_ROOT, GID_OPERATOR, 0666, "%s%d", tmpstr, unit); resource_int_value("pwc", unit, "power_save", &sc->power_save); sc->stats = 0; resource_int_value("pwc", unit, "stats", &sc->stats); sc->led_on = 100; resource_int_value("pwc", unit, "led_on", &sc->led_on); if(sc->led_on < 0) sc->led_on = 0; sc->led_off = 0; resource_int_value("pwc", unit, "led_off", &sc->led_off); if(sc->led_off < 0) sc->led_off = 0; sc->vframes = 5; resource_int_value("pwc", unit, "fps", &sc->vframes); if(sc->vframes < 4 || sc->vframes > 30) sc->vframes = 5; #ifdef USE_MMAP sc->pwc_mbufs = 2; /* Default number of mmap() buffers */ #else sc->pwc_mbufs = 1; #endif resource_int_value("pwc", unit, "mbufs", &sc->pwc_mbufs); if(sc->pwc_mbufs < 1 || sc->pwc_mbufs > MAX_IMAGES) sc->pwc_mbufs = 1; sc->pwc_fbufs = 3; /* Default number of frame buffers */ resource_int_value("pwc", unit, "fbufs", &sc->pwc_fbufs); if(sc->pwc_fbufs < 2 || sc->pwc_fbufs > MAX_FRAMES) sc->pwc_fbufs = 2; sc->vcompression = 2; /* 0..3 = uncompressed..high */ resource_int_value("pwc", unit, "compression", &sc->vcompression); if(sc->vcompression > 3) sc->vcompression = 3; sc->vsize = PSZ_CIF; /* XXX default format, was PSZ_SIF */ tmpstr = NULL; resource_string_value("pwc", unit, "size", &tmpstr); if(tmpstr != NULL) { for (i = 0; i < PSZ_MAX; i++) { if (!strcmp(pwc_image_sizes[i].name, tmpstr)) { /* Found! */ sc->vsize = i; break; } } } if(sc->vsize == PSZ_VGA && sc->vframes > 15) sc->vframes = 15; sc->pwc_pad = 0; /* XXX only exact formats */ resource_int_value("pwc", unit, "pad", &sc->pwc_pad); if (sc->camera_cb.cb_attach) sc->camera_cb.cb_attach(sc); device_printf(sc->sc_dev, "%s %s (USB webcam) type %d\n", sc->pwc_info.name, sc->serial, sc->pwc_info.type); device_printf(sc->sc_dev, " sensor %d (%s) bridge %d (%s)\n", sc->pwc_info.sensor, sensor_name(sc->pwc_info.sensor), sc->pwc_info.bridge, bridge_name(sc->pwc_info.bridge)); usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->udev,USBDEV(sc->sc_dev)); return 0; /* success */ } static void close_videopipe(struct pwc_softc *sc) { #ifdef NEW_USB #warning using stack version 2 usbd_transfer_unsetup(sc->xfer, MAX_ISOC_TRANSFERS); #else if(sc->sc_videopipe != NULL) { usbd_abort_pipe(sc->sc_videopipe); usbd_close_pipe(sc->sc_videopipe); sc->sc_videopipe = NULL; } #endif } static int pwc_detach(device_t dev) { struct pwc_softc *sc = device_get_softc(dev); again: Trace(TRACE_PROBE,"pwc_detach: sc=%p\n",sc); close_videopipe(sc); sc->error_status = EPIPE; if(sc->vopen) { if(sc->state & PWC_ASLEEP) { wakeup(sc); } if(sc->state & PWC_POLL) { sc->state &= ~PWC_POLL; selwakeuppri(&sc->rsel,PZERO); } device_printf(sc->sc_dev, "Disconnected while webcam is in use!\n"); usb_detach_wait(USBDEV(sc->sc_dev)); goto again; } if(sc->sc_dev_t != NULL) destroy_dev(sc->sc_dev_t); mtx_destroy(&sc->ptrlock); pwc_free_buffers(sc,1); usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->udev,USBDEV(sc->sc_dev)); return 0; } /* * open handler. Perform the following tasks: * - turn on the camera; * - allocate any necessary buffer (frame, decompressor, isoc buffers); * - initialize bridge and sensor as needed for the required format * - start ISOC transfers */ int pwc_open(struct cdev *dev, int flag, int mode, usb_proc_ptr p) { int i,err; int old_vframes; int unit = PWCUNIT(dev); struct pwc_softc *sc = devclass_get_softc(pwc_devclass, unit); if (sc == NULL) return ENXIO; Trace(TRACE_OPEN,"pwc_open: flag=%d, mode=%d, unit=%d\n", flag, mode, unit); if(sc->error_status == EPIPE) return sc->error_status; mtx_lock(&sc->ptrlock); if (sc->vopen) { /* XXX could well use an atomic instruction */ mtx_unlock(&sc->ptrlock); return EBUSY; } sc->vopen = 1; /* protect against multiple opens */ mtx_unlock(&sc->ptrlock); /* When opening, try the last video mode, whose id is saved in sc->vframes. * Store it in a local variable as the callback will override it * XXX the fallback mode should be in the try_video_mode handler. */ old_vframes = sc->vframes; /* Invoke the open callback if present */ if (sc->camera_cb.cb_open && (err = sc->camera_cb.cb_open(sc)) ) goto bad; err = ENOMEM; /* Allocate frame buffer structure */ i = sc->pwc_fbufs * sizeof(struct pwc_frame_buf); sc->fbuf = malloc(i, M_USBDEV, M_WAITOK); if (sc->fbuf == NULL) { device_printf(sc->sc_dev, "Failed to allocate frame buffer structure.\n"); goto bad; } memset(sc->fbuf, 0, i); /* create frame buffers, and make circular ring */ for (i = 0; i < sc->pwc_fbufs; i++) { sc->fbuf[i].data = malloc(PWC_FRAME_SIZE,M_USBDEV,M_WAITOK); if (sc->fbuf[i].data == NULL) { device_printf(sc->sc_dev, "Failed to allocate frame buffer\n"); goto bad; } memset(sc->fbuf[i].data, 128, PWC_FRAME_SIZE); sc->fbuf[i].filled = 0; } /* Allocate image buffer */ if(sc->image_data == NULL) { sc->image_data = malloc(sc->pwc_mbufs * round_page(sc->len_per_image), M_USBDEV, M_WAITOK); if(sc->image_data == NULL) { device_printf(sc->sc_dev, "Failed to allocate image buffers\n"); goto bad; } } for(i = 0; i < sc->pwc_mbufs; i++) { sc->images[i].bufmem = ((char*)sc->image_data) + i * round_page(sc->len_per_image); sc->images[i].vma_use_count = 0; } for (; i < MAX_IMAGES; i++) sc->images[i].bufmem = NULL; for (i = 0; i < sc->pwc_mbufs; i++) sc->image_used[i] = 0; #ifndef NEW_USB /* Allocate iso transfers */ for (i = 0; i < MAX_ISOC_TRANSFERS; i++) { sc->sbuf[i].sc = sc; sc->xfer[i] = usbd_alloc_xfer(sc->udev); if(sc->xfer[i] == NULL) { device_printf(sc->sc_dev, "Failed to allocate transfer\n"); goto bad; } sc->sbuf[i].data = usbd_alloc_buffer(sc->xfer[i], ISO_BUFFER_SIZE); if(sc->sbuf[i].data == NULL) { device_printf(sc->sc_dev, "Failed to allocate transfer buffer %d\n", i); goto bad; } } #endif sc->state = 0; sc->frames = 0; sc->start_ticks = ticks; sc->byte_count = 0; sc->bytes_skipped = 0; sc->vframe_count = 0; sc->vframes_dumped = 0; sc->vframes_error = 0; sc->visoc_errors = 0; sc->error_status = 0; sc->vsnapshot = 0; /* Try the last used video */ sc->state |= PWC_INIT; err = -pwc_try_video_mode(sc, pwc_image_sizes[sc->vsize].xy.x, pwc_image_sizes[sc->vsize].xy.y, old_vframes, sc->vcompression, 0 /* no snapshot */); sc->state &= ~PWC_INIT; init_jpeg_decoder(&sc->spca50x); Trace(TRACE_OPEN, "open complete with err %d\n", err); if(err == 0) return 0; bad: pwc_free_buffers(sc,0); sc->vopen = 0; return err; } /* Important: close doesn't get called after a detach */ int pwc_close(struct cdev *dev, int flag, int mode, usb_proc_ptr p) { int unit = PWCUNIT(dev); struct pwc_softc *sc = devclass_get_softc(pwc_devclass, unit); /* NULL check ? */ int d = ticks - sc->start_ticks; if (d <= 0) d++; if (sc->frames == 0) sc->frames = 1; printf("%d bytes skipped\n", sc->bytes_skipped); printf("%d frames (%d dumped) in %d ticks, %d fps %d visoc_errors %d bytes %d bpf\n", sc->frames, sc->vframes_dumped, d, (sc->frames * hz * 1000) / d, sc->visoc_errors, sc->byte_count, sc->byte_count / sc->frames); Trace(TRACE_OPEN,"pwc_close: flag=%d, mode=%d, unit=%d\n", flag, mode, unit); printf("called close routine\n"); /* Dump statistics, but only if a reasonable amount of frames were processed (to prevent endless log-entries in case of snap-shot programs) */ if(sc->vframe_count > 20 && sc->stats) { device_printf(sc->sc_dev, "%d frames received, dumped %d frames, %d frames with errors.\n", sc->vframe_count, sc->vframes_dumped, sc->vframes_error); } close_videopipe(sc); if(sc->error_status != EPIPE) { /* if not unplugged, call the camera-specific shutdown */ if (sc->camera_cb.cb_close) sc->camera_cb.cb_close(sc); } pwc_free_buffers(sc,0); sc->vopen = 0; usb_detach_wakeup(USBDEV(sc->sc_dev)); /* XXX ? */ return 0; } /* * Read handler. Assume the ISOC engine is up and running, all we have to do * is possibly wait for a new frame, and return the data. * Once done, advance the frame pointers to the next frame. * At the moment there is no attempt here to do any error recovery, * in case this belongs to the interrupt service routine. */ int pwc_read(struct cdev *dev, struct uio *uio, int flag) { int unit = PWCUNIT(dev); struct pwc_softc *sc = devclass_get_softc(pwc_devclass, unit); int bytes_to_read; int count = uio->uio_resid; int err; Trace(TRACE_READ,"pwc_read: flag=%d, unit=%d\n", flag, unit); if (sc->error_status) return sc->error_status; /* In case we're doing partial reads, we don't have to wait for a frame */ if(sc->image_read_pos == 0) { while ((err = pwc_handle_frame(sc)) == -EBUSY) { if(flag & O_NONBLOCK) return EWOULDBLOCK; sc->state |= PWC_ASLEEP; err = tsleep(sc, PZERO | PCATCH, "pwcrd", 0); sc->state &= ~PWC_ASLEEP; if(sc->error_status) return sc->error_status; else if(err) return err; } if(err < 0) return -err; } if(sc->vpalette == VIDEO_PALETTE_RAW) bytes_to_read = sc->frame_size; else bytes_to_read = sc->view.size; /* copy bytes to user space; we allow for partial reads */ if(count + sc->image_read_pos > bytes_to_read) count = bytes_to_read - sc->image_read_pos; Trace(TRACE_READ_VERBOSE, "pwc_read: wants: %d bytes, reading: %d bytes\n",uio->uio_resid,count); err = uiomove(sc->images[sc->fill_image].bufmem + sc->image_read_pos,count,uio); if(err) return err; sc->image_read_pos += count; if(sc->image_read_pos >= bytes_to_read) { /* All data has been read */ sc->frames++; sc->image_read_pos = 0; /* move to next image */ sc->image_used[sc->fill_image] = 0; sc->fill_image = (sc->fill_image + 1) % sc->pwc_mbufs; } return 0; } int pwc_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, usb_proc_ptr p) { int unit = PWCUNIT(dev); struct pwc_softc *sc = devclass_get_softc(pwc_devclass, unit); Trace(TRACE_READ,"pwc_ioctl: cmd 0x%lu addr %p\n", cmd, addr); if (sc->error_status) return sc->error_status; return -pwc_video_do_ioctl(sc,cmd,addr,unit); } int pwc_poll(struct cdev *dev, int events, usb_proc_ptr p) { int unit = PWCUNIT(dev); struct pwc_softc *sc = devclass_get_softc(pwc_devclass, unit); int revents = 0; Trace(TRACE_READ,"pwc_poll: events %d\n", events); if(sc->error_status) return sc->error_status; if (events & (POLLIN | POLLRDNORM)) { mtx_lock(&sc->ptrlock); if(sc->full_frames == NULL) { sc->state |= PWC_POLL; selrecord(p,&sc->rsel); Trace(TRACE_READ,"pwc_poll: will block\n"); } else { revents |= events & (POLLIN | POLLRDNORM); Trace(TRACE_READ,"pwc_poll: not blocking\n"); } mtx_unlock(&sc->ptrlock); } if (events & (POLLOUT | POLLWRNORM)) { /* write is not allowed, so no point blocking on it */ revents |= events & (POLLOUT | POLLWRNORM); } return revents; } int pwc_mmap(struct cdev *dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot) { #ifdef USE_MMAP int unit = PWCUNIT(dev); struct pwc_softc *sc = devclass_get_softc(pwc_devclass, unit); if(sc == NULL) return ENXIO; if (sc->error_status) return sc->error_status; if(nprot & PROT_EXEC || sc->image_data == NULL || offset < 0 || offset >= sc->pwc_mbufs * round_page(sc->len_per_image)) return -1; *paddr = vtophys(((char*)sc->image_data) + offset); Trace(TRACE_MEMORY,"pwc_mmap: phys: 0x%08x offset: 0x%08x\n",*paddr,offset); return 0; #else return ENXIO; #endif } #ifdef NEW_USB /* * Called whenever there is something significant for a * read trasnfer, e.g. error, setup, or data ready */ static void pwc_isoc_read_callback(struct usbd_xfer *xfer) { struct pwc_softc *sc = xfer->priv_sc; int i; /* buffer number */ uint16_t isize = sc->vmax_packet_size; /* each buffer is this big */ USBD_CHECK_STATUS(xfer); /* demux for the interrupt reason */ tr_transferred: for (i = 0; i < MAX_ISOC_TRANSFERS; i++) { if (xfer->frlengths[i] == 0) continue; sc->consume(sc, (unsigned char *)xfer->buffer + isize * i, xfer->frlengths[i]); } tr_setup: tr_error: for(i = 0; i < MAX_ISOC_TRANSFERS; i++) { /* setup size for next transfer */ xfer->frlengths[i] = isize; } usbd_start_hardware(xfer); return; } #else /* !NEW_USB */ static void setup_xfer(struct pwc_iso_buf *req, usbd_xfer_handle xfer) { /* setup size for next transfer */ struct pwc_softc *sc = req->sc; int i; for (i = 0; i < ISO_FRAMES_PER_DESC; i++) req->sizes[i] = sc->vmax_packet_size; usbd_setup_isoc_xfer(xfer, sc->sc_videopipe, req, req->sizes, ISO_FRAMES_PER_DESC,USBD_NO_COPY, pwc_isoc_handler); usbd_transfer(xfer); } #endif /* !NEW_USB */ int pwc_try_video_mode(struct pwc_softc *sc, int width, int height, int new_fps, int new_compression, int new_snapshot) { usb_endpoint_descriptor_t *found = NULL, *edesc = NULL; int i, err, ret; close_videopipe(sc); pwc_reset_buffers(sc); /* Try to set video mode... if that fails fallback to previous mode */ ret = pwc_set_video_mode(sc, width, height, new_fps, new_compression, new_snapshot); if(ret < 0) { Trace(TRACE_FLOW, "pwc_set_video_mode attempt 1 failed.\n"); err = pwc_set_video_mode(sc,sc->view.x,sc->view.y,sc->vframes,sc->vcompression,sc->vsnapshot); if(err < 0) { Trace(TRACE_FLOW, "pwc_set_video_mode attempt 2 failed.\n"); return err; } } sc->drop_frames++; /* try to avoid garbage during switch */ sc->vsync = VSYNC_NOCOPY; err = set_alt_interface(sc->udev, sc->sc_iface,sc->valternate); if(err != USBD_NORMAL_COMPLETION) { device_printf(sc->sc_dev, "Failed to set alternate interface to: %d (error %d)\n", sc->valternate,err); return -err; } if(sc->sc_iface == NULL || sc->sc_iface->idesc == NULL) { device_printf(sc->sc_dev, "Failed to get endpoint count\n"); return -EINVAL; } for (i = 0; i < sc->sc_iface->idesc->bNumEndpoints; i++) { edesc = usbd_interface2endpoint_descriptor(sc->sc_iface, i); if (edesc) Trace(TRACE_IOCTL, "desc %d endpoint %d size %d\n", i, UE_GET_ADDR(edesc->bEndpointAddress), UGETW(edesc->wMaxPacketSize)); if(edesc != NULL && UE_GET_ADDR(edesc->bEndpointAddress) == sc->vendpoint) found = edesc; } if(found == NULL) { device_printf(sc->sc_dev, "Failed to find videoendpoint %d\n", sc->vendpoint); return -EINVAL; } edesc = found; i = UGETW(edesc->wMaxPacketSize); /* bit 0-10 are the miniframe, 11-12 the count+1 */ sc->vmax_packet_size = (i & 0x7ff) * (1 + ((i>>11) & 3) ); printf("endpoint %d frame size %d\n", sc->vendpoint, sc->vmax_packet_size); if(sc->vmax_packet_size < 0 || sc->vmax_packet_size > ISO_MAX_FRAME_SIZE) { device_printf(sc->sc_dev, "Invalid packetsize (%d) for endpoint %d\n", sc->vmax_packet_size,edesc->bEndpointAddress); return -EINVAL; } /* XXX for 2.0 (fast) pipes maybe i need to call usbd_setup_pipe directly ? */ #ifdef NEW_USB mtx_assert(&sc->sc_mtx, MA_OWNED); if(!(sc->state & UGEN_OPEN_IN) && sc->pipe_in) { struct usbd_config usbd_config[1] = { /* zero */ }; usbd_config[0].type = ed->bmAttributes & UE_XFERTYPE; usbd_config[0].endpoint = ed->bEndpointAddress & UE_ADDR; usbd_config[0].direction = UE_DIR_IN; usbd_config[0].timeout = sc->in_timeout; switch(ed->bmAttributes & UE_XFERTYPE) { default: printf("sorry can only work on ISOC pipes"); return -EINVAL; break; case UE_ISOCHRONOUS: isize = UGETW(ed->wMaxPacketSize); sc->in_frames = ISO_FRAMES_PER_DESC; if(usbd_get_speed(sc->sc_udev) == USB_SPEED_HIGH) sce->in_frames *= 8; if(ugen_allocate_blocks(sc, sce, UGEN_RD_CFG, &sce->in_queue, sce->in_frames * 10, isize) == 0) return ENOMEM; usbd_config[0].flags = USBD_SHORT_XFER_OK; usbd_config[0].bufsize = isize * sce->in_frames; usbd_config[0].frames = sce->in_frames; usbd_config[0].callback = ugenisoc_read_callback; usbd_config[0].timeout = 0; err = __usbd_transfer_setup(sc, sce, UGEN_RD_CFG, sc->sc_udev, sce->pipe_in->iface_index, &sce->xfer_in[0], &usbd_config[0], 1); if(!err) { err = __usbd_transfer_setup(sc, sce, UGEN_RD_CFG, sc->sc_udev, sce->pipe_in->iface_index, &sce->xfer_in[1], &usbd_config[0], 1); } if(err) { usbd_transfer_unsetup(&sce->xfer_in[0], 1); usbd_transfer_unsetup(&sce->xfer_in[1], 1); ugen_free_blocks(&sce->in_queue); return (EIO); } usbd_transfer_start(sce->xfer_in[0]); usbd_transfer_start(sce->xfer_in[1]); printf("isoc open done\n"); break; } } #else printf("speed %d attributes %d\n", sc->sc_iface->device->speed, edesc->bmAttributes); err = usbd_open_pipe(sc->sc_iface,edesc->bEndpointAddress, 0, &sc->sc_videopipe); if(err != USBD_NORMAL_COMPLETION) { device_printf(sc->sc_dev, "Failed to open videopipe (error %d)\n", err); return -err; } for (i = 0; i < MAX_ISOC_TRANSFERS; i++) { setup_xfer(&sc->sbuf[i], sc->xfer[i]); } #endif Trace(TRACE_FLOW, "try_video_mode done\n"); if(sc->state & PWC_INIT) return 0; return ret; } /* * Prepare for the next frame, appending the current one * to the full list, and grab one from the free list * (if available) or from the full list if nothing free. */ int pwc_next_fill_frame(struct pwc_softc *sc) { int ret = 0; mtx_lock(&sc->ptrlock); if(sc->fill_frame != NULL) { /* append to 'full' list */ if (sc->full_frames == NULL) sc->full_frames = sc->fill_frame; else sc->full_frames_tail->next = sc->fill_frame; sc->full_frames_tail = sc->fill_frame; } if (sc->empty_frames != NULL) { /* We have empty frames available. That's easy */ sc->fill_frame = sc->empty_frames; sc->empty_frames = sc->empty_frames->next; } else { /* Hmm. Take it from the full list */ sc->fill_frame = sc->full_frames; sc->full_frames = sc->full_frames->next; ret = 1; /* report the frame dumped above */ sc->vframes_dumped++; if (sc->vframe_count > FRAME_LOWMARK && sc->vframes_dumped <= 20) Trace(TRACE_ISOC,"Dumping frame %d%s.\n", sc->vframe_count, sc->vframes_dumped == 20 ? " (last message)" : ""); } sc->fill_frame->next = NULL; mtx_unlock(&sc->ptrlock); return ret; } static void pwc_isoc_handler(usbd_xfer_handle xfer, usbd_private_handle addr,usbd_status status) { struct pwc_iso_buf *req = addr; struct pwc_softc *sc = req->sc; u_int32_t count; int awake = 0; int i; usbd_get_xfer_status(xfer, NULL, NULL, &count, NULL); Trace(TRACE_ISOC_VERBOSE, "pwc_isoc_handler: status=%d count=%u\n",status,count); if (status == USBD_CANCELLED) { Trace(TRACE_ISOC, "pwc_isoc_handler: status = cancelled\n"); return; } if(status != USBD_NORMAL_COMPLETION) { Trace(TRACE_ISOC, "pwc_isoc_handler called with status: %d\n",status); if (++sc->visoc_errors > MAX_ISOC_ERRORS) { if(sc->error_status != EIO) device_printf(sc->sc_dev, "Too many ISOC errors, bailing out.\n"); sc->error_status = EIO; awake = 1; } } else { /* normal completion */ /* Reset ISOC error counter. We did get here, after all. */ // sc->visoc_errors = 0; /* Consume data */ for (i = 0; i < ISO_FRAMES_PER_DESC; i++) { sc->byte_count += req->sizes[i]; awake |= sc->camera_cb.cb_consume(sc, req->data + sc->vmax_packet_size * i, req->sizes[i]); } } if(awake) { if(sc->state & PWC_ASLEEP) { wakeup(sc); } if(sc->state & PWC_POLL) { sc->state &= ~PWC_POLL; selwakeuppri(&sc->rsel, PZERO); } } /* setup size for next transfer */ setup_xfer(req, xfer); } int pwc_handle_frame(struct pwc_softc *sc) { int ret = 0; unsigned char *yuv; char *image; mtx_lock(&sc->ptrlock); /* First grab our read_frame; this is removed from all lists, so we can release the lock after this without problems */ if (sc->full_frames == NULL) { mtx_unlock(&sc->ptrlock); return -EBUSY; } sc->read_frame = sc->full_frames; sc->full_frames = sc->full_frames->next; sc->read_frame->next = NULL; /* Decompression is a lenghty process, so it's outside of the lock. * This gives the isoc_handler the opportunity to fill more frames * in the mean time. */ mtx_unlock(&sc->ptrlock); image = sc->images[sc->fill_image].bufmem; if (image == NULL) return -EFAULT; yuv = ((char*)sc->read_frame->data) + sc->frame_header_size; /* skip header */ /* Raw format; that's easy... */ if (sc->vpalette == VIDEO_PALETTE_RAW) { memcpy(image, yuv, sc->frame_size); } else if (sc->camera_cb.cb_decompress) { ret = sc->camera_cb.cb_decompress(sc, yuv, image, sc->read_frame->filled); } mtx_lock(&sc->ptrlock); /* We're done with read_buffer, tack it to the end of the empty buffer list */ if(sc->empty_frames == NULL) { sc->empty_frames = sc->read_frame; sc->empty_frames_tail = sc->empty_frames; } else { sc->empty_frames_tail->next = sc->read_frame; sc->empty_frames_tail = sc->read_frame; } sc->read_frame = NULL; mtx_unlock(&sc->ptrlock); return ret; } /* * Reset the full_frames list, link all frames in the * empty_frames list, and take the first one for fill_frame. */ static void pwc_reset_buffers(struct pwc_softc *sc) { int i; mtx_lock(&sc->ptrlock); sc->full_frames = NULL; sc->full_frames_tail = NULL; for (i = 0; i < sc->pwc_fbufs; i++) { sc->fbuf[i].filled = 0; if (i > 0) sc->fbuf[i].next = &sc->fbuf[i - 1]; else sc->fbuf->next = NULL; } sc->empty_frames = &sc->fbuf[sc->pwc_fbufs - 1]; sc->empty_frames_tail = sc->fbuf; sc->read_frame = NULL; sc->fill_frame = sc->empty_frames; sc->empty_frames = sc->empty_frames->next; sc->image_read_pos = 0; sc->fill_image = 0; mtx_unlock(&sc->ptrlock); } static void pwc_free_buffers(struct pwc_softc *sc, int detach) { int i; Trace(TRACE_MEMORY, "Entering free_buffers(%p).\n", sc); if (sc->fbuf != NULL) { for (i = 0; i < sc->pwc_fbufs; i++) { if (sc->fbuf[i].data != NULL) { free(sc->fbuf[i].data,M_USBDEV); sc->fbuf[i].data = NULL; } } free(sc->fbuf,M_USBDEV); sc->fbuf = NULL; } if (sc->decompress_data != NULL) { free(sc->decompress_data,M_USBDEV); sc->decompress_data = NULL; } #ifdef USE_MMAP if(sc->image_data != NULL && detach == 1) #else if(sc->image_data != NULL) #endif { free(sc->image_data,M_USBDEV); sc->image_data = NULL; } #ifdef NEW_USB #warning must free xfer buffers #else for (i = 0; i < MAX_ISOC_TRANSFERS; i++) { if (sc->xfer[i] != NULL) { usbd_free_xfer(sc->xfer[i]); /* implicit buffer free */ sc->xfer[i] = NULL; } } #endif } DRIVER_MODULE(pwc, uhub, pwc_driver, pwc_devclass, usbd_driver_load, 0); pwcbsd/pwcbsd/linux_usbif.h000644 000423 000000 00000011364 10552522123 016562 0ustar00luigiwheel000000 000000 /* * linux_usbif.h - Linux API; usb_control_msg() usb_set_interface() * usb_set_configuration() * Inspired by code from * Copyright (C) 2003,2004,2005,2006 Takafumi Mizuno * * $Id: linux_usbif.h,v 1.12 2007/01/13 16:38:30 luigi Exp $ */ #ifndef _LINUX_USBIF_H #define _LINUX_USBIF_H /* standard FreeBSD headers */ #include #include /* memcpy */ #include #include /* XXX ? */ #include #include #include /* XXX ? */ #include /* XXX ? */ #include #include #include #include #include #include /* usb headers */ #include #include /* usbd_device_handle */ #include /* usbd_device */ #include #include /* video4linux header, and some typedefs */ #include "videodev.h" typedef unsigned int uint; typedef uint8_t u8; typedef uint64_t u64; /* from $kernel_source_top/include/linux/???.h */ #define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) /* fake a high version so MODULE_USE_COUNT is not used */ #define LINUX_VERSION_CODE KERNEL_VERSION(2, 6, 8) #define __KERNEL__ /* simulate kernel */ #define ERESTARTSYS ERESTART #define ENOIOCTLCMD EINVAL /* * XXX fixes for things not defined in FreeBSD */ #if !defined(PWC_H) /* XXX only for the spca.. */ #define printk(fmt , args...) printf(fmt , ##args) #define warn(X...) printf(X) #define dbg(X...) printf(X) #define err(X...) printf(X) #define info(fmt, X...) printf(fmt "\n" , ##X) #define wait_ms(x) tsleep("spca", PCATCH, "xx", x*HZ/1000) #define udelay(x) tsleep("spca", PCATCH, "xx", x*HZ/1000000) /* if you really need malloc/free, uncomment this but with care. */ // #define kmalloc(siz,opt) malloc(siz, M_USBDEV, M_WAITOK) // #define kfree(pt) free(pt, M_USBDEV) #endif /* from $kernel_source_top/include/linux/module.h */ #define MODULE_AUTHOR(name) #define MODULE_LICENSE(license) #define MODULE_DESCRIPTION(desc) //#define MODULE_SUPPORTED_DEVICE(name) #define MODULE_PARM(var,type) #define MODULE_PARM_DESC(var,desc) #define MODULE_DEVICE_TABLE(type,name) /* from $kernel_source_top/include/linux/wait.h */ /* no action for queue */ // #define init_waitqueue_head(q) do { } while (0) #define waitqueue_active(q) 0 #define wake_up_interruptible(q) do { } while (0) /* from $kernel_source_top/include/asm-$arch/semaphore.h */ /* no action for semaphore */ // #define down(lock) do { } while (0) // #define up(lock) do { } while (0) // #define init_MUTEX(lock) do { } while (0) /* from $kernel_source_top/include/asm-$arch/spinlock.h */ typedef void * spinlock_t; #define spin_lock_irqsave(lock, flags) { int __unused i = flags; } #define spin_unlock_irqrestore(lock, flags) #define spin_lock(sem) do { } while (0) #define spin_unlock(sem) do { } while (0) // #define SPIN_LOCK_UNLOCKED 0 /* from $kernel_source_top/include/asm-$arch/hardirq.h */ /* no idea */ #define in_interrupt() 0 #define __devinitdata const /* attribute used in linux headers */ struct usb_device_id { __u16 idVendor; __u16 idProduct; }; #define USB_DEVICE(vend,prod) (vend), (prod) // #define USB_ENDPOINT_DIR_MASK 0x80 #define USB_DIR_OUT UT_WRITE #define USB_DIR_IN UT_READ #define USB_TYPE_VENDOR UT_VENDOR #define USB_RECIP_DEVICE UT_DEVICE #define USB_RECIP_INTERFACE UT_INTERFACE // #define USB_RECIP_ENDPOINT UT_ENDPOINT // #define USB_RECIP_OTHER UT_OTHER // #define USB_ENDPOINT_XFERTYPE_MASK UE_XFERTYPE // #define USB_ENDPOINT_XFER_CONTROL UE_CONTROL // #define USB_ENDPOINT_XFER_ISOC UE_ISOCHRONOUS // #define USB_ENDPOINT_XFER_BULK UE_BULK // #define USB_ENDPOINT_XFER_INT UE_INTERRUPT #define USB_REQ_SET_INTERFACE UR_SET_INTERFACE #define HZ hz /* on FreeBSD... */ #define usb_sndctrlpipe(a,b) (b) #define usb_rcvctrlpipe(a,b) (b) #define usb_sndintpipe(a,b) (b) #define usb_rcvintpipe(a,b) (b) #define usb_sndisocpipe(a,b) (b) #define usb_rcvisocpipe(a,b) (b) // #define usb_rcvbulkpipe(a,b) (b) #define usb_sndbulkpipe(a,b) (b) /* * This is a trick to make spca routines to reference * the usbd_device_handle without having to change all * interfaces. We are lucky that both are structs. */ #define usb_device usbd_device /* XXX */ #endif /* _LINUX_USBIF_H */ pwcbsd/pwcbsd/pwc-ctrl.c000644 000423 000000 00000115660 10553461760 015777 0ustar00luigiwheel000000 000000 /* * PWCBSD - Philips USB webcam driver for FreeBSD 5.4 and higher * * Copyright (C) 2006 Raaf * * Based on the Linux pwc driver. * * Copyright (C) 1999-2003 Nemosoft Unv. * Copyright (C) 2004-2006 Luc Saillard * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * 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 */ /* $Id: pwc-ctrl.c,v 1.25 2007/01/17 14:37:23 luigi Exp $ */ /* Control functions for the cam; brightness, contrast, video mode, etc. */ #include "pwc.h" #include "pwc-ioctl.h" #include "pwc-uncompress.h" #include "pwc-kiara.h" #include "pwc-timon.h" #include "pwc-dec1.h" #include "pwc-dec23.h" /* Request types: video */ #define SET_LUM_CTL 0x01 #define GET_LUM_CTL 0x02 #define SET_CHROM_CTL 0x03 #define GET_CHROM_CTL 0x04 #define SET_STATUS_CTL 0x05 #define GET_STATUS_CTL 0x06 #define SET_EP_STREAM_CTL 0x07 #define GET_EP_STREAM_CTL 0x08 #define SET_MPT_CTL 0x0D #define GET_MPT_CTL 0x0E /* Selectors for the Luminance controls [GS]ET_LUM_CTL */ #define AGC_MODE_FORMATTER 0x2000 #define PRESET_AGC_FORMATTER 0x2100 #define SHUTTER_MODE_FORMATTER 0x2200 #define PRESET_SHUTTER_FORMATTER 0x2300 #define PRESET_CONTOUR_FORMATTER 0x2400 #define AUTO_CONTOUR_FORMATTER 0x2500 #define BACK_LIGHT_COMPENSATION_FORMATTER 0x2600 #define CONTRAST_FORMATTER 0x2700 #define DYNAMIC_NOISE_CONTROL_FORMATTER 0x2800 #define FLICKERLESS_MODE_FORMATTER 0x2900 #define AE_CONTROL_SPEED 0x2A00 #define BRIGHTNESS_FORMATTER 0x2B00 #define GAMMA_FORMATTER 0x2C00 /* Selectors for the Chrominance controls [GS]ET_CHROM_CTL */ #define WB_MODE_FORMATTER 0x1000 #define AWB_CONTROL_SPEED_FORMATTER 0x1100 #define AWB_CONTROL_DELAY_FORMATTER 0x1200 #define PRESET_MANUAL_RED_GAIN_FORMATTER 0x1300 #define PRESET_MANUAL_BLUE_GAIN_FORMATTER 0x1400 #define COLOUR_MODE_FORMATTER 0x1500 #define SATURATION_MODE_FORMATTER1 0x1600 #define SATURATION_MODE_FORMATTER2 0x1700 /* Selectors for the Status controls [GS]ET_STATUS_CTL */ #define SAVE_USER_DEFAULTS_FORMATTER 0x0200 #define RESTORE_USER_DEFAULTS_FORMATTER 0x0300 #define RESTORE_FACTORY_DEFAULTS_FORMATTER 0x0400 #define READ_AGC_FORMATTER 0x0500 #define READ_SHUTTER_FORMATTER 0x0600 #define READ_RED_GAIN_FORMATTER 0x0700 #define READ_BLUE_GAIN_FORMATTER 0x0800 #define SENSOR_TYPE_FORMATTER1 0x0C00 #define GET_STATUS_3000 0x3000 #define READ_RAW_Y_MEAN_FORMATTER 0x3100 #define SET_POWER_SAVE_MODE_FORMATTER 0x3200 #define MIRROR_IMAGE_FORMATTER 0x3300 #define LED_FORMATTER 0x3400 #define LOWLIGHT 0x3500 #define GET_STATUS_3600 0x3600 #define SENSOR_TYPE_FORMATTER2 0x3700 #define GET_STATUS_4100 0x4100 /* Get */ /* Formatters for the Video Endpoint controls [GS]ET_EP_STREAM_CTL */ #define VIDEO_OUTPUT_CONTROL_FORMATTER 0x0100 /* Formatters for the motorized pan & tilt [GS]ET_MPT_CTL */ #define PT_RELATIVE_CONTROL_FORMATTER 0x01 #define PT_RESET_CONTROL_FORMATTER 0x02 #define PT_STATUS_FORMATTER 0x03 /********/ /* Entries for the Nala (645/646) camera; the Nala doesn't have compression preferences, so you either get compressed or non-compressed streams. An alternate value of 0 means this mode is not available at all. */ struct Nala_table_entry { char alternate; /* USB alternate setting */ int compressed; /* Compressed yes/no */ unsigned char mode[3]; /* precomputed mode table */ }; static struct Nala_table_entry Nala_table[PSZ_MAX][8] = { #include "pwc-nala.h" }; static void pwc_set_image_buffer_size(struct pwc_device *pdev); int set_alt_interface(struct usbd_device *udev, usbd_interface_handle iface, int alt) { #ifdef NEW_USB return usbreq_set_interface(udev, iface->idesc->bInterfaceNumber, alt); #else return usbd_set_interface(iface, alt); #endif } /****************************************************************************/ /* * Send a control message. * XXX The 'pipe' parameter is ignored, which can be a problem. * XXX timeout was not present in the original, but it is in ms anyways. */ int usb_control_msg(usbd_device_handle udev,u_int pipe,u_int8_t request,u_int8_t requesttype, u_int16_t value, u_int16_t index, void *data, u_int16_t size,int timeout) { Trace(TRACE_FLOW, "usb_control_msg pipe %d req 0x%02x type 0x%02x val 0x%04x index 0x%04x data %p size %d\n", pipe, request, requesttype, value, index, data, size); usb_device_request_t req; if (pipe != 0) printf("warning, usb_control_msg for pipe %d\n", pipe); if (0) printf("usb_control_msg req %x ty 0x%x val 0x%x idx 0x%x size %d to %d\n", request, requesttype, value, index, size, timeout); req.bRequest = request; req.bmRequestType = requesttype; USETW(req.wValue, value); USETW(req.wIndex, index); USETW(req.wLength,size); //return -usbd_do_request_flags_pipe(udev, udev->default_pipe, &req, data, 0, NULL, timeout); return -usbd_do_request_flags(udev, &req, data, 0, NULL, timeout); } /* XXX warning, these two use a hardwired name 'buf' for the result variable. */ #define SendControlMsg(request, value, buflen) \ usb_control_msg(pdev->udev, 0, \ request, \ UT_WRITE_VENDOR_DEVICE, \ value, \ pdev->vcinterface, \ &buf, buflen, 500) #define RecvControlMsg(request, value, buflen) \ usb_control_msg(pdev->udev, 0, \ request, \ UT_READ_VENDOR_DEVICE, \ value, \ pdev->vcinterface, \ &buf, buflen, 500) static inline int send_video_command(usbd_device_handle udev, int index, void *buf, int buflen) { return usb_control_msg(udev, 0, SET_EP_STREAM_CTL, UT_WRITE_VENDOR_DEVICE, VIDEO_OUTPUT_CONTROL_FORMATTER, index, buf, buflen, 1000); } static inline int set_video_mode_Nala(struct pwc_device *pdev, int size, int frames) { unsigned char buf[3]; int ret, fps; struct Nala_table_entry *pEntry; int frames2frames[31] = { /* closest match of framerate */ 0, 0, 0, 0, 4, /* 0-4 */ 5, 5, 7, 7, 10, /* 5-9 */ 10, 10, 12, 12, 15, /* 10-14 */ 15, 15, 15, 20, 20, /* 15-19 */ 20, 20, 20, 24, 24, /* 20-24 */ 24, 24, 24, 24, 24, /* 25-29 */ 24 /* 30 */ }; int frames2table[31] = { 0, 0, 0, 0, 0, /* 0-4 */ 1, 1, 1, 2, 2, /* 5-9 */ 3, 3, 4, 4, 4, /* 10-14 */ 5, 5, 5, 5, 5, /* 15-19 */ 6, 6, 6, 6, 7, /* 20-24 */ 7, 7, 7, 7, 7, /* 25-29 */ 7 /* 30 */ }; if (size < 0 || size > PSZ_CIF || frames < 4 || frames > 25) return -EINVAL; frames = frames2frames[frames]; fps = frames2table[frames]; pEntry = &Nala_table[size][fps]; if (pEntry->alternate == 0) return -EINVAL; memcpy(buf, pEntry->mode, 3); ret = send_video_command(pdev->udev, pdev->vendpoint, buf, 3); if (ret < 0) { Err("Failed to send video command... %d\n", ret); return ret; } if (pEntry->compressed && pdev->vpalette != VIDEO_PALETTE_RAW) { switch(pdev->pwc_info.type) { case 645: case 646: pwc_dec1_init(pdev->pwc_info.type, pdev->release, buf, pdev->decompress_data); break; case 675: case 680: case 690: case 720: case 730: case 740: case 750: pwc_dec23_init(pdev->pwc_info.type, pdev->release, buf, pdev->decompress_data); break; } } pdev->cmd_len = 3; memcpy(pdev->cmd_buf, buf, 3); /* Set various parameters */ pdev->vframes = frames; pdev->vsize = size; pdev->valternate = pEntry->alternate; pdev->image = pwc_image_sizes[size].xy; pdev->frame_size = (pdev->image.x * pdev->image.y * 3) / 2; if (pEntry->compressed) { if (pdev->release < 5) { /* 4 fold compression */ pdev->vbandlength = 528; pdev->frame_size /= 4; } else { pdev->vbandlength = 704; pdev->frame_size /= 3; } } else pdev->vbandlength = 0; return 0; } static inline int set_video_mode_Timon(struct pwc_device *pdev, int size, int frames, int compression, int snapshot) { unsigned char buf[13]; const struct Timon_table_entry *pChoose; int ret, fps; if (size >= PSZ_MAX || frames < 5 || frames > 30 || compression < 0 || compression > 3) return -EINVAL; if (size == PSZ_VGA && frames > 15) return -EINVAL; fps = (frames / 5) - 1; /* Find a supported framerate with progressively higher compression ratios if the preferred ratio is not available. */ pChoose = NULL; while (compression <= 3) { pChoose = &Timon_table[size][fps][compression]; if (pChoose->alternate != 0) break; compression++; } if (pChoose == NULL || pChoose->alternate == 0) return -ENOENT; /* Not supported. */ memcpy(buf, pChoose->mode, 13); if (snapshot) buf[0] |= 0x80; ret = send_video_command(pdev->udev, pdev->vendpoint, buf, 13); if (ret < 0) return ret; if (pChoose->bandlength > 0 && pdev->vpalette != VIDEO_PALETTE_RAW) pwc_dec23_init(pdev->pwc_info.type, pdev->release, buf, pdev->decompress_data); pdev->cmd_len = 13; memcpy(pdev->cmd_buf, buf, 13); /* Set various parameters */ pdev->vframes = frames; pdev->vsize = size; pdev->vsnapshot = snapshot; pdev->valternate = pChoose->alternate; pdev->image = pwc_image_sizes[size].xy; pdev->vbandlength = pChoose->bandlength; if (pChoose->bandlength > 0) pdev->frame_size = (pChoose->bandlength * pdev->image.y) / 4; else pdev->frame_size = (pdev->image.x * pdev->image.y * 12) / 8; return 0; } static inline int set_video_mode_Kiara(struct pwc_device *pdev, int size, int frames, int compression, int snapshot) { const struct Kiara_table_entry *pChoose = NULL; int fps, ret; unsigned char buf[12]; struct Kiara_table_entry RawEntry = {6, 773, 1272, {0xAD, 0xF4, 0x10, 0x27, 0xB6, 0x24, 0x96, 0x02, 0x30, 0x05, 0x03, 0x80}}; if (size >= PSZ_MAX || frames < 5 || frames > 30 || compression < 0 || compression > 3) return -EINVAL; if (size == PSZ_VGA && frames > 15) return -EINVAL; fps = (frames / 5) - 1; /* special case: VGA @ 5 fps and snapshot is raw bayer mode */ if (size == PSZ_VGA && frames == 5 && snapshot) { /* Only available in case the raw palette is selected or we have the decompressor available. This mode is only available in compressed form */ if (pdev->vpalette == VIDEO_PALETTE_RAW) { Info("Choosing VGA/5 BAYER mode (%d).\n", pdev->vpalette); pChoose = &RawEntry; } else { Info("VGA/5 BAYER mode _must_ have a decompressor available, or use RAW palette.\n"); } } else { /* Find a supported framerate with progressively higher compression ratios if the preferred ratio is not available. Skip this step when using RAW modes. */ while (compression <= 3) { pChoose = &Kiara_table[size][fps][compression]; if (pChoose->alternate != 0) break; compression++; } } if (pChoose == NULL || pChoose->alternate == 0) return -ENOENT; /* Not supported. */ Debug("Using alternate setting %d.\n", pChoose->alternate); /* usb_control_msg won't take staticly allocated arrays as argument?? */ memcpy(buf, pChoose->mode, 12); if (snapshot) buf[0] |= 0x80; /* Firmware bug: video endpoint is 5, but commands are sent to endpoint 4 */ ret = send_video_command(pdev->udev, 4 /* pdev->vendpoint */, buf, 12); if (ret < 0) return ret; if (pChoose->bandlength > 0 && pdev->vpalette != VIDEO_PALETTE_RAW) pwc_dec23_init(pdev->pwc_info.type, pdev->release, buf, pdev->decompress_data); pdev->cmd_len = 12; memcpy(pdev->cmd_buf, buf, 12); /* All set and go */ pdev->vframes = frames; pdev->vsize = size; pdev->vsnapshot = snapshot; pdev->valternate = pChoose->alternate; pdev->image = pwc_image_sizes[size].xy; pdev->vbandlength = pChoose->bandlength; if (pdev->vbandlength > 0) pdev->frame_size = (pdev->vbandlength * pdev->image.y) / 4; else pdev->frame_size = (pdev->image.x * pdev->image.y * 12) / 8; Debug("frame_size=%d, vframes=%d, vsize=%d, vsnapshot=%d, vbandlength=%d\n", pdev->frame_size,pdev->vframes,pdev->vsize,pdev->vsnapshot,pdev->vbandlength); return 0; } /** @pdev: device structure @width: viewport width @height: viewport height @frame: framerate, in fps @compression: preferred compression ratio @snapshot: snapshot mode or streaming */ int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, int frames, int compression, int snapshot) { int ret; struct pwc_format *size; Trace(TRACE_FLOW, "set_video_mode(%dx%d @ %d, palette %d).\n", width, height, frames, pdev->vpalette); size = pwc_decode_size(pdev, width, height); if (size == NULL) { Debug("Could not find suitable size.\n"); return -EINVAL; } Debug("decode_size = %d.\n", size->id); ret = -EINVAL; switch(pdev->pwc_info.type) { case 0: /* spca cameras */ spca50x_stop_isoc(&pdev->spca50x); ret = spca5xx_setMode(&pdev->spca50x, size->xy.x, size->xy.y, pdev->vpalette); spca50x_set_packet_size(&pdev->spca50x, pdev->spca50x.pipe_size); pdev->valternate = pdev->spca50x.alt; printf("spca camera, call spca5xx_setMode alt now %d\n", pdev->valternate); ret = spca50x_init_source(&pdev->spca50x); // ret = spca5xx_setMode(&pdev->spca50x, width, height, pdev->vpalette); spcaCameraStart(&pdev->spca50x); pdev->spca50x.streaming = 1; pdev->frame_size = (width * height * 3)/2; /* mode 420 */ pdev->vframes = 2; /* XXX */ pdev->image = size->xy; break; case 645: case 646: ret = set_video_mode_Nala(pdev, size->id, frames); break; case 675: case 680: case 690: ret = set_video_mode_Timon(pdev, size->id, frames, compression, snapshot); break; case 720: case 730: case 740: case 750: ret = set_video_mode_Kiara(pdev, size->id, frames, compression, snapshot); break; } if (ret < 0) { if (ret == -ENOENT) Info("Video mode %s@%d fps is only supported with the decompressor module (pwcx).\n", size->name, frames); else { device_printf(pdev->sc_dev, "Failed to set video mode to %s@%d fps; return code = %d\n", size->name, frames, -ret); } return ret; } pdev->view.x = width; pdev->view.y = height; pdev->frame_total_size = pdev->frame_size + pdev->frame_header_size + pdev->frame_trailer_size; pwc_set_image_buffer_size(pdev); Trace(TRACE_SIZE, "Set viewport to %dx%d, image size is %dx%d.\n", width, height, size->xy.x, size->xy.y); return 0; } static void pwc_set_image_buffer_size(struct pwc_device *pdev) { int i, factor = 0, filler = 0; /* for PALETTE_YUV420P */ switch(pdev->vpalette) { case VIDEO_PALETTE_YUV420P: factor = 6; filler = 128; break; case VIDEO_PALETTE_RAW: factor = 6; /* can be uncompressed YUV420P */ filler = 0; break; } /* Set sizes in bytes */ pdev->image.size = pdev->image.x * pdev->image.y * factor / 4; pdev->view.size = pdev->view.x * pdev->view.y * factor / 4; /* Align offset, or you'll get some very weird results in YUV420 mode... x must be multiple of 4 (to get the Y's in place), and y even (or you'll mixup U & V). This is less of a problem for YUV420P. */ pdev->offset.x = ((pdev->view.x - pdev->image.x) / 2) & 0xFFFC; pdev->offset.y = ((pdev->view.y - pdev->image.y) / 2) & 0xFFFE; /* Fill buffers with gray or black */ for (i = 0; i < pdev->pwc_mbufs; i++) { if (pdev->images[i].bufmem != NULL) memset(pdev->images[i].bufmem, filler, pdev->view.size); } } /* BRIGHTNESS */ int pwc_get_brightness(struct pwc_device *pdev) { unsigned char buf; int ret; if (pdev->pwc_info.type == 0) { printf("pwc_get_brightness gives %d\n", pdev->spca50x.brightness); return pdev->spca50x.brightness; } /* XXX only 7 bit for brightness on the pwc */ ret = RecvControlMsg(GET_LUM_CTL, BRIGHTNESS_FORMATTER, 1); if (ret < 0) return ret; return buf << 9; } int pwc_set_brightness(struct pwc_device *pdev, int value) { char buf; if (pdev->pwc_info.type == 0) return 0; /* XXX done already */ if (value < 0) value = 0; if (value > 0xffff) value = 0xffff; buf = (value >> 9) & 0x7f; return SendControlMsg(SET_LUM_CTL, BRIGHTNESS_FORMATTER, 1); } /* CONTRAST */ int pwc_get_contrast(struct pwc_device *pdev) { char buf; int ret; if (pdev->pwc_info.type == 0) return pdev->spca50x.contrast; ret = RecvControlMsg(GET_LUM_CTL, CONTRAST_FORMATTER, 1); if (ret < 0) return ret; return buf; } int pwc_set_contrast(struct pwc_device *pdev, int value) { char buf; if (pdev->pwc_info.type == 0) /* done already in the std handler */ return 0; if (value < 0) value = 0; if (value > 0xffff) value = 0xffff; buf = (value >> 10) & 0x3f; return SendControlMsg(SET_LUM_CTL, CONTRAST_FORMATTER, 1); } /* GAMMA */ int pwc_get_gamma(struct pwc_device *pdev) { char buf; int ret; if (pdev->pwc_info.type == 0) /* done already in the std handler */ return 0; /* XXX what ? */ ret = RecvControlMsg(GET_LUM_CTL, GAMMA_FORMATTER, 1); if (ret < 0) return ret; return buf; } int pwc_set_gamma(struct pwc_device *pdev, int value) { char buf; if (pdev->pwc_info.type == 0) /* done already in the std handler */ return 0; if (value < 0) value = 0; if (value > 0xffff) value = 0xffff; buf = (value >> 11) & 0x1f; return SendControlMsg(SET_LUM_CTL, GAMMA_FORMATTER, 1); } /* SATURATION */ int pwc_get_saturation(struct pwc_device *pdev) { char buf; int ret; if (pdev->pwc_info.type == 0) /* done already in the std handler */ return 0; /* XXX */ if (pdev->pwc_info.type < 675) return -1; ret = RecvControlMsg(GET_CHROM_CTL, pdev->pwc_info.type < 730 ? SATURATION_MODE_FORMATTER2 : SATURATION_MODE_FORMATTER1, 1); if (ret < 0) return ret; return buf+256; } int pwc_set_saturation(struct pwc_device *pdev, int value) { char buf; if (pdev->pwc_info.type == 0) /* done already in the std handler */ return 0; /* XXX */ if (pdev->pwc_info.type < 675) return -EINVAL; if (value < 0) value = 0; if (value > 0xffff) value = 0xffff; /* saturation ranges from -100 to +100 */ buf = (value - 32768) / 327; return SendControlMsg(SET_CHROM_CTL, pdev->pwc_info.type < 730 ? SATURATION_MODE_FORMATTER2 : SATURATION_MODE_FORMATTER1, 1); } /* AGC */ int pwc_set_agc(struct pwc_device *pdev, int mode, int value) { char buf; int ret; if (pdev->pwc_info.type == 0) /* done already in the std handler */ return 0; /* XXX */ if (mode) buf = 0x0; /* auto */ else buf = 0xff; /* fixed */ ret = SendControlMsg(SET_LUM_CTL, AGC_MODE_FORMATTER, 1); if (!mode && ret >= 0) { if (value < 0) value = 0; if (value > 0xffff) value = 0xffff; buf = (value >> 10) & 0x3F; ret = SendControlMsg(SET_LUM_CTL, PRESET_AGC_FORMATTER, 1); } if (ret < 0) return ret; return 0; } int pwc_get_agc(struct pwc_device *pdev, int *value) { unsigned char buf; int ret; if (pdev->pwc_info.type == 0) /* done already in the std handler */ return 0; /* XXX */ ret = RecvControlMsg(GET_LUM_CTL, AGC_MODE_FORMATTER, 1); if (ret < 0) return ret; if (buf != 0) { /* fixed */ ret = RecvControlMsg(GET_LUM_CTL, PRESET_AGC_FORMATTER, 1); if (ret < 0) return ret; if (buf > 0x3F) buf = 0x3F; *value = (buf << 10); } else { /* auto */ ret = RecvControlMsg(GET_STATUS_CTL, READ_AGC_FORMATTER, 1); if (ret < 0) return ret; /* Gah... this value ranges from 0x00 ... 0x9F */ if (buf > 0x9F) buf = 0x9F; *value = -(48 + buf * 409); } return 0; } int pwc_set_shutter_speed(struct pwc_device *pdev, int mode, int value) { char buf[2]; int speed, ret; if (pdev->pwc_info.type == 0) /* done already in the std handler */ return 0; /* XXX */ if (mode) buf[0] = 0x0; /* auto */ else buf[0] = 0xff; /* fixed */ ret = SendControlMsg(SET_LUM_CTL, SHUTTER_MODE_FORMATTER, 1); if (!mode && ret >= 0) { if (value < 0) value = 0; if (value > 0xffff) value = 0xffff; switch(pdev->pwc_info.type) { case 675: case 680: case 690: /* speed ranges from 0x0 to 0x290 (656) */ speed = (value / 100); buf[1] = speed >> 8; buf[0] = speed & 0xff; break; case 720: case 730: case 740: case 750: /* speed seems to range from 0x0 to 0xff */ buf[1] = 0; buf[0] = value >> 8; break; } ret = SendControlMsg(SET_LUM_CTL, PRESET_SHUTTER_FORMATTER, 2); } return ret; } int pwc_get_shutter_speed(struct pwc_device *pdev, int *value) { unsigned char buf[2]; int ret; if (pdev->pwc_info.type == 0) /* done already in the std handler */ return 0; /* XXX */ ret = RecvControlMsg(GET_STATUS_CTL, READ_SHUTTER_FORMATTER, 2); if (ret < 0) return ret; *value = buf[0] + (buf[1] << 8); switch(pdev->pwc_info.type) { case 675: case 680: case 690: /* speed ranges from 0x0 to 0x290 (656) */ *value *= 100; break; case 720: case 730: case 740: case 750: /* speed seems to range from 0x0 to 0xff */ *value <<= 8; break; } return 0; } /* POWER */ int pwc_camera_power(struct pwc_device *pdev, int power) { char buf; if (pdev->pwc_info.type < 675 || (pdev->pwc_info.type < 730 && pdev->release < 6)) return 0; /* Not supported by Nala or Timon < release 6 */ if (power) buf = 0x00; /* active */ else buf = 0xFF; /* power save */ return SendControlMsg(SET_STATUS_CTL, SET_POWER_SAVE_MODE_FORMATTER, 1); } /* private calls */ inline int pwc_restore_user(struct pwc_device *pdev) { char buf; /* dummy */ return SendControlMsg(SET_STATUS_CTL, RESTORE_USER_DEFAULTS_FORMATTER, 0); } inline int pwc_save_user(struct pwc_device *pdev) { char buf; /* dummy */ return SendControlMsg(SET_STATUS_CTL, SAVE_USER_DEFAULTS_FORMATTER, 0); } inline int pwc_restore_factory(struct pwc_device *pdev) { char buf; /* dummy */ return SendControlMsg(SET_STATUS_CTL, RESTORE_FACTORY_DEFAULTS_FORMATTER, 0); } /* ************************************************* */ /* Patch by Alvarado: (not in the original version */ /* * the camera recognizes modes from 0 to 4: * * 00: indoor (incandescant lighting) * 01: outdoor (sunlight) * 02: fluorescent lighting * 03: manual * 04: auto */ int pwc_set_awb(struct pwc_device *pdev, int mode) { char buf; int ret; if (mode < 0) mode = 0; if (mode > 4) mode = 4; buf = mode & 0x07; /* just the lowest three bits */ ret = SendControlMsg(SET_CHROM_CTL, WB_MODE_FORMATTER, 1); if (ret < 0) return ret; return 0; } int pwc_get_awb(struct pwc_device *pdev) { unsigned char buf; int ret; ret = RecvControlMsg(GET_CHROM_CTL, WB_MODE_FORMATTER, 1); if (ret < 0) return ret; return buf; } int pwc_set_red_gain(struct pwc_device *pdev, int value) { unsigned char buf; if (value < 0) value = 0; if (value > 0xffff) value = 0xffff; /* only the msb is considered */ buf = value >> 8; return SendControlMsg(SET_CHROM_CTL, PRESET_MANUAL_RED_GAIN_FORMATTER, 1); } int pwc_get_red_gain(struct pwc_device *pdev, int *value) { unsigned char buf; int ret; ret = RecvControlMsg(GET_CHROM_CTL, PRESET_MANUAL_RED_GAIN_FORMATTER, 1); if (ret < 0) return ret; *value = buf << 8; return 0; } int pwc_set_blue_gain(struct pwc_device *pdev, int value) { unsigned char buf; if (value < 0) value = 0; if (value > 0xffff) value = 0xffff; /* only the msb is considered */ buf = value >> 8; return SendControlMsg(SET_CHROM_CTL, PRESET_MANUAL_BLUE_GAIN_FORMATTER, 1); } int pwc_get_blue_gain(struct pwc_device *pdev, int *value) { unsigned char buf; int ret; ret = RecvControlMsg(GET_CHROM_CTL, PRESET_MANUAL_BLUE_GAIN_FORMATTER, 1); if (ret < 0) return ret; *value = buf << 8; return 0; } /* The following two functions are different, since they only read the internal red/blue gains, which may be different from the manual gains set or read above. */ static inline int pwc_read_red_gain(struct pwc_device *pdev, int *value) { unsigned char buf; int ret; ret = RecvControlMsg(GET_STATUS_CTL, READ_RED_GAIN_FORMATTER, 1); if (ret < 0) return ret; *value = buf << 8; return 0; } static inline int pwc_read_blue_gain(struct pwc_device *pdev, int *value) { unsigned char buf; int ret; ret = RecvControlMsg(GET_STATUS_CTL, READ_BLUE_GAIN_FORMATTER, 1); if (ret < 0) return ret; *value = buf << 8; return 0; } static inline int pwc_set_wb_speed(struct pwc_device *pdev, int speed) { unsigned char buf; /* useful range is 0x01..0x20 */ buf = speed / 0x7f0; return SendControlMsg(SET_CHROM_CTL, AWB_CONTROL_SPEED_FORMATTER, 1); } static inline int pwc_get_wb_speed(struct pwc_device *pdev, int *value) { unsigned char buf; int ret; ret = RecvControlMsg(GET_CHROM_CTL, AWB_CONTROL_SPEED_FORMATTER, 1); if (ret < 0) return ret; *value = buf * 0x7f0; return 0; } static inline int pwc_set_wb_delay(struct pwc_device *pdev, int delay) { unsigned char buf; /* useful range is 0x01..0x3F */ buf = (delay >> 10); return SendControlMsg(SET_CHROM_CTL, AWB_CONTROL_DELAY_FORMATTER, 1); } static inline int pwc_get_wb_delay(struct pwc_device *pdev, int *value) { unsigned char buf; int ret; ret = RecvControlMsg(GET_CHROM_CTL, AWB_CONTROL_DELAY_FORMATTER, 1); if (ret < 0) return ret; *value = buf << 10; return 0; } int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value) { unsigned char buf[2]; if (pdev->pwc_info.type < 730) return 0; on_value /= 100; off_value /= 100; if (on_value < 0) on_value = 0; if (on_value > 0xff) on_value = 0xff; if (off_value < 0) off_value = 0; if (off_value > 0xff) off_value = 0xff; buf[0] = on_value; buf[1] = off_value; return SendControlMsg(SET_STATUS_CTL, LED_FORMATTER, 2); } int pwc_get_leds(struct pwc_device *pdev, int *on_value, int *off_value) { unsigned char buf[2]; int ret; if (pdev->pwc_info.type < 730) { *on_value = -1; *off_value = -1; return 0; } ret = RecvControlMsg(GET_STATUS_CTL, LED_FORMATTER, 2); if (ret < 0) return ret; *on_value = buf[0] * 100; *off_value = buf[1] * 100; return 0; } static inline int pwc_set_contour(struct pwc_device *pdev, int contour) { unsigned char buf; int ret; if (contour < 0) buf = 0xff; /* auto contour on */ else buf = 0x0; /* auto contour off */ ret = SendControlMsg(SET_LUM_CTL, AUTO_CONTOUR_FORMATTER, 1); if (ret < 0) return ret; if (contour < 0) return 0; if (contour > 0xffff) contour = 0xffff; buf = (contour >> 10); /* contour preset is [0..3f] */ ret = SendControlMsg(SET_LUM_CTL, PRESET_CONTOUR_FORMATTER, 1); if (ret < 0) return ret; return 0; } static inline int pwc_get_contour(struct pwc_device *pdev, int *contour) { unsigned char buf; int ret; ret = RecvControlMsg(GET_LUM_CTL, AUTO_CONTOUR_FORMATTER, 1); if (ret < 0) return ret; if (buf == 0) { /* auto mode off, query current preset value */ ret = RecvControlMsg(GET_LUM_CTL, PRESET_CONTOUR_FORMATTER, 1); if (ret < 0) return ret; *contour = buf << 10; } else *contour = -1; return 0; } static inline int pwc_set_backlight(struct pwc_device *pdev, int backlight) { unsigned char buf; if (backlight) buf = 0xff; else buf = 0x0; return SendControlMsg(SET_LUM_CTL, BACK_LIGHT_COMPENSATION_FORMATTER, 1); } static inline int pwc_get_backlight(struct pwc_device *pdev, int *backlight) { int ret; unsigned char buf; ret = RecvControlMsg(GET_LUM_CTL, BACK_LIGHT_COMPENSATION_FORMATTER, 1); if (ret < 0) return ret; *backlight = buf; return 0; } static inline int pwc_set_flicker(struct pwc_device *pdev, int flicker) { unsigned char buf; if (flicker) buf = 0xff; else buf = 0x0; return SendControlMsg(SET_LUM_CTL, FLICKERLESS_MODE_FORMATTER, 1); } static inline int pwc_get_flicker(struct pwc_device *pdev, int *flicker) { int ret; unsigned char buf; ret = RecvControlMsg(GET_LUM_CTL, FLICKERLESS_MODE_FORMATTER, 1); if (ret < 0) return ret; *flicker = buf; return 0; } static inline int pwc_set_dynamic_noise(struct pwc_device *pdev, int noise) { unsigned char buf; if (noise < 0) noise = 0; if (noise > 3) noise = 3; buf = noise; return SendControlMsg(SET_LUM_CTL, DYNAMIC_NOISE_CONTROL_FORMATTER, 1); } static inline int pwc_get_dynamic_noise(struct pwc_device *pdev, int *noise) { int ret; unsigned char buf; ret = RecvControlMsg(GET_LUM_CTL, DYNAMIC_NOISE_CONTROL_FORMATTER, 1); if (ret < 0) return ret; *noise = buf; return 0; } static inline int _pwc_mpt_reset(struct pwc_device *pdev, int flags) { unsigned char buf; buf = flags & 0x03; // only lower two bits are currently used return SendControlMsg(SET_MPT_CTL, PT_RESET_CONTROL_FORMATTER, 1); } int pwc_mpt_reset(struct pwc_device *pdev, int flags) { int ret; ret = _pwc_mpt_reset(pdev, flags); if (ret >= 0) { pdev->pan_angle = 0; pdev->tilt_angle = 0; } return ret; } static inline int _pwc_mpt_set_angle(struct pwc_device *pdev, int pan, int tilt) { unsigned char buf[4]; /* set new relative angle; angles are expressed in degrees * 100, but cam as .5 degree resolution, hence devide by 200. Also the angle must be multiplied by 64 before it's send to the cam (??) */ pan = 64 * pan / 100; tilt = -64 * tilt / 100; /* positive tilt is down, which is not what the user would expect */ buf[0] = pan & 0xFF; buf[1] = (pan >> 8) & 0xFF; buf[2] = tilt & 0xFF; buf[3] = (tilt >> 8) & 0xFF; return SendControlMsg(SET_MPT_CTL, PT_RELATIVE_CONTROL_FORMATTER, 4); } int pwc_mpt_set_angle(struct pwc_device *pdev, int pan, int tilt) { int ret; /* check absolute ranges */ if (pan < pdev->angle_range.pan_min || pan > pdev->angle_range.pan_max || tilt < pdev->angle_range.tilt_min || tilt > pdev->angle_range.tilt_max) return -ERANGE; /* go to relative range, check again */ pan -= pdev->pan_angle; tilt -= pdev->tilt_angle; /* angles are specified in degrees * 100, thus the limit = 36000 */ if (pan < -36000 || pan > 36000 || tilt < -36000 || tilt > 36000) return -ERANGE; ret = _pwc_mpt_set_angle(pdev, pan, tilt); if (ret >= 0) { pdev->pan_angle += pan; pdev->tilt_angle += tilt; } if (ret == -USBD_STALLED) /* stall -> out of range */ ret = -ERANGE; return ret; } static inline int pwc_mpt_get_status(struct pwc_device *pdev, struct pwc_mpt_status *status) { int ret; unsigned char buf[5]; ret = RecvControlMsg(GET_MPT_CTL, PT_STATUS_FORMATTER, 5); if (ret < 0) return ret; status->status = buf[0] & 0x7; // 3 bits are used for reporting status->time_pan = (buf[1] << 8) + buf[2]; status->time_tilt = (buf[3] << 8) + buf[4]; return 0; } int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor) { unsigned char buf; int ret = -1, request; if (pdev->pwc_info.type < 675) request = SENSOR_TYPE_FORMATTER1; else if (pdev->pwc_info.type < 730) return -1; /* The Vesta series doesn't have this call */ else request = SENSOR_TYPE_FORMATTER2; ret = RecvControlMsg(GET_STATUS_CTL, request, 1); if (ret < 0) return ret; if (pdev->pwc_info.type < 675) *sensor = buf | 0x100; else *sensor = buf; return 0; } int pwc_set_colour_mode(struct pwc_device *pdev, int colour) { unsigned char buf; if (colour) buf = 0xff; else buf = 0x0; return SendControlMsg(SET_CHROM_CTL, COLOUR_MODE_FORMATTER, 1); } int pwc_get_colour_mode(struct pwc_device *pdev, int *colour) { int ret; unsigned char buf; ret = RecvControlMsg(GET_CHROM_CTL, COLOUR_MODE_FORMATTER, 1); if (ret < 0) return ret; *colour = !!buf; return 0; } /* End of Add-Ons */ /* ************************************************* */ /* define local variable for arg */ #define ARG_DEF(ARG_type, ARG_name)\ ARG_type *ARG_name = arg; /* copy arg to local variable */ #define ARG_IN(ARG_name) /* nothing */ /* argument itself (referenced) */ #define ARGR(ARG_name) (*ARG_name) /* argument address */ #define ARGA(ARG_name) ARG_name /* copy local variable to arg */ #define ARG_OUT(ARG_name) /* nothing */ int pwc_do_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg) { int ret = 0; switch(cmd) { case VIDIOCPWCRUSER: if (pwc_restore_user(pdev)) ret = -EINVAL; break; case VIDIOCPWCSUSER: if (pwc_save_user(pdev)) ret = -EINVAL; break; case VIDIOCPWCFACTORY: if (pwc_restore_factory(pdev)) ret = -EINVAL; break; case VIDIOCPWCSCQUAL: { ARG_DEF(int, qual) ARG_IN(qual) if (ARGR(qual) < 0 || ARGR(qual) > 3) ret = -EINVAL; else ret = pwc_try_video_mode(pdev, pdev->view.x, pdev->view.y, pdev->vframes, ARGR(qual), pdev->vsnapshot); if (ret >= 0) pdev->vcompression = ARGR(qual); break; } case VIDIOCPWCGCQUAL: { ARG_DEF(int, qual) ARGR(qual) = pdev->vcompression; ARG_OUT(qual) break; } case VIDIOCPWCPROBE: { ARG_DEF(struct pwc_probe, probe) strncpy(ARGR(probe).name, pdev->pwc_info.name,sizeof(ARGR(probe).name) - 1); ARGR(probe).name[sizeof(ARGR(probe).name) - 1] = '\0'; ARGR(probe).type = pdev->pwc_info.type; ARG_OUT(probe) break; } case VIDIOCPWCGSERIAL: { ARG_DEF(struct pwc_serial, serial) strcpy(ARGR(serial).serial, pdev->serial); ARG_OUT(serial) break; } case VIDIOCPWCSAGC: { ARG_DEF(int, agc) ARG_IN(agc) if (pwc_set_agc(pdev, ARGR(agc) < 0 ? 1 : 0, ARGR(agc))) ret = -EINVAL; break; } case VIDIOCPWCGAGC: { ARG_DEF(int, agc) if (pwc_get_agc(pdev, ARGA(agc))) ret = -EINVAL; ARG_OUT(agc) break; } case VIDIOCPWCGSHUTTER: { ARG_DEF(int,shutter_speed) if(pwc_get_shutter_speed(pdev,ARGA(shutter_speed))) ret = -EINVAL; ARG_OUT(shutter_speed); break; } case VIDIOCPWCSSHUTTER: { ARG_DEF(int, shutter_speed) ARG_IN(shutter_speed) ret = pwc_set_shutter_speed(pdev, ARGR(shutter_speed) < 0 ? 1 : 0, ARGR(shutter_speed)); break; } case VIDIOCPWCSAWB: { ARG_DEF(struct pwc_whitebalance, wb) ARG_IN(wb) ret = pwc_set_awb(pdev, ARGR(wb).mode); if (ret >= 0 && ARGR(wb).mode == PWC_WB_MANUAL) { pwc_set_red_gain(pdev, ARGR(wb).manual_red); pwc_set_blue_gain(pdev, ARGR(wb).manual_blue); } break; } case VIDIOCPWCGAWB: { ARG_DEF(struct pwc_whitebalance, wb) memset(ARGA(wb), 0, sizeof(struct pwc_whitebalance)); ARGR(wb).mode = pwc_get_awb(pdev); if (ARGR(wb).mode < 0) ret = -EINVAL; else { if (ARGR(wb).mode == PWC_WB_MANUAL) { ret = pwc_get_red_gain(pdev, &ARGR(wb).manual_red); if (ret < 0) break; ret = pwc_get_blue_gain(pdev, &ARGR(wb).manual_blue); if (ret < 0) break; } if (ARGR(wb).mode == PWC_WB_AUTO) { ret = pwc_read_red_gain(pdev, &ARGR(wb).read_red); if (ret < 0) break; ret =pwc_read_blue_gain(pdev, &ARGR(wb).read_blue); if (ret < 0) break; } } ARG_OUT(wb) break; } case VIDIOCPWCSAWBSPEED: { ARG_DEF(struct pwc_wb_speed, wbs) if (ARGR(wbs).control_speed > 0) { ret = pwc_set_wb_speed(pdev, ARGR(wbs).control_speed); } if (ARGR(wbs).control_delay > 0) { ret = pwc_set_wb_delay(pdev, ARGR(wbs).control_delay); } break; } case VIDIOCPWCGAWBSPEED: { ARG_DEF(struct pwc_wb_speed, wbs) ret = pwc_get_wb_speed(pdev, &ARGR(wbs).control_speed); if (ret < 0) break; ret = pwc_get_wb_delay(pdev, &ARGR(wbs).control_delay); if (ret < 0) break; ARG_OUT(wbs) break; } case VIDIOCPWCSLED: { ARG_DEF(struct pwc_leds, leds) ARG_IN(leds) ret = pwc_set_leds(pdev, ARGR(leds).led_on, ARGR(leds).led_off); break; } case VIDIOCPWCGLED: { ARG_DEF(struct pwc_leds, leds) ret = pwc_get_leds(pdev, &ARGR(leds).led_on, &ARGR(leds).led_off); ARG_OUT(leds) break; } case VIDIOCPWCSCONTOUR: { ARG_DEF(int, contour) ARG_IN(contour) ret = pwc_set_contour(pdev, ARGR(contour)); break; } case VIDIOCPWCGCONTOUR: { ARG_DEF(int, contour) ret = pwc_get_contour(pdev, ARGA(contour)); ARG_OUT(contour) break; } case VIDIOCPWCSBACKLIGHT: { ARG_DEF(int, backlight) ARG_IN(backlight) ret = pwc_set_backlight(pdev, ARGR(backlight)); break; } case VIDIOCPWCGBACKLIGHT: { ARG_DEF(int, backlight) ret = pwc_get_backlight(pdev, ARGA(backlight)); ARG_OUT(backlight) break; } case VIDIOCPWCSFLICKER: { ARG_DEF(int, flicker) ARG_IN(flicker) ret = pwc_set_flicker(pdev, ARGR(flicker)); break; } case VIDIOCPWCGFLICKER: { ARG_DEF(int, flicker) ret = pwc_get_flicker(pdev, ARGA(flicker)); ARG_OUT(flicker) break; } case VIDIOCPWCSDYNNOISE: { ARG_DEF(int, dynnoise) ARG_IN(dynnoise) ret = pwc_set_dynamic_noise(pdev, ARGR(dynnoise)); break; } case VIDIOCPWCGDYNNOISE: { ARG_DEF(int, dynnoise) ret = pwc_get_dynamic_noise(pdev, ARGA(dynnoise)); ARG_OUT(dynnoise); break; } case VIDIOCPWCSCOLOUR: { ARG_DEF(int, colour) ARG_IN(colour) ret = pwc_set_colour_mode(pdev, ARGR(colour)); break; } case VIDIOCPWCGCOLOUR: { ARG_DEF(int, colour) ret = pwc_get_colour_mode(pdev, ARGA(colour)); ARG_OUT(colour); break; } case VIDIOCPWCGREALSIZE: { ARG_DEF(struct pwc_imagesize, size) ARGR(size).width = pdev->image.x; ARGR(size).height = pdev->image.y; ARG_OUT(size) break; } case VIDIOCPWCMPTRESET: if (pdev->features & FEATURE_MOTOR_PANTILT) { ARG_DEF(int, flags) ARG_IN(flags) ret = pwc_mpt_reset(pdev, ARGR(flags)); } else { ret = -ENXIO; } break; case VIDIOCPWCMPTGRANGE: if (pdev->features & FEATURE_MOTOR_PANTILT) { ARG_DEF(struct pwc_mpt_range, range) ARGR(range) = pdev->angle_range; ARG_OUT(range) } else { ret = -ENXIO; } break; case VIDIOCPWCMPTSANGLE: { int new_pan, new_tilt; if (pdev->features & FEATURE_MOTOR_PANTILT) { ARG_DEF(struct pwc_mpt_angles, angles) ARG_IN(angles) /* The camera can only set relative angles, so do some calculations when getting an absolute angle . */ if (ARGR(angles).absolute) { new_pan = ARGR(angles).pan; new_tilt = ARGR(angles).tilt; } else { new_pan = pdev->pan_angle + ARGR(angles).pan; new_tilt = pdev->tilt_angle + ARGR(angles).tilt; } ret = pwc_mpt_set_angle(pdev, new_pan, new_tilt); } else { ret = -ENXIO; } break; } case VIDIOCPWCMPTGANGLE: if (pdev->features & FEATURE_MOTOR_PANTILT) { ARG_DEF(struct pwc_mpt_angles, angles) ARGR(angles).absolute = 1; ARGR(angles).pan = pdev->pan_angle; ARGR(angles).tilt = pdev->tilt_angle; ARG_OUT(angles) } else { ret = -ENXIO; } break; case VIDIOCPWCMPTSTATUS: if (pdev->features & FEATURE_MOTOR_PANTILT) { ARG_DEF(struct pwc_mpt_status, status) ret = pwc_mpt_get_status(pdev, ARGA(status)); ARG_OUT(status) } else { ret = -ENXIO; } break; case VIDIOCPWCGVIDCMD: { ARG_DEF(struct pwc_video_command, cmd); ARGR(cmd).type = pdev->pwc_info.type; ARGR(cmd).release = pdev->release; ARGR(cmd).command_len = pdev->cmd_len; memcpy(&ARGR(cmd).command_buf, pdev->cmd_buf, pdev->cmd_len); ARGR(cmd).bandlength = pdev->vbandlength; ARGR(cmd).frame_size = pdev->frame_size; ARG_OUT(cmd) break; } /* case VIDIOCPWCGVIDTABLE: { ARG_DEF(struct pwc_table_init_buffer, table); ARGR(table).len = pdev->cmd_len; memcpy(&ARGR(table).buffer, pdev->decompress_data, pdev->decompressor->table_size); ARG_OUT(table) break; } */ default: ret = -ENOTTY; break; } if (ret > 0) return 0; return ret; } pwcbsd/pwcbsd/pwc-dec1.c000644 000423 000000 00000002101 10546676137 015640 0ustar00luigiwheel000000 000000 /* * PWCBSD - Philips USB webcam driver for FreeBSD 5.4 and higher * * Copyright (C) 2006 Raaf * * Based on the Linux pwc driver. * * Copyright (C) 1999-2003 Nemosoft Unv. * Copyright (C) 2004-2006 Luc Saillard * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * 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 "pwc-dec1.h" void pwc_dec1_init(int type, int release, void *buffer, void *table) { } void pwc_dec1_exit(void) { } pwcbsd/pwcbsd/pwc-dec1.h000644 000423 000000 00000002126 10546676137 015654 0ustar00luigiwheel000000 000000 /* * PWCBSD - Philips USB webcam driver for FreeBSD 5.4 and higher * * Copyright (C) 2006 Raaf * * Based on the Linux pwc driver. * * Copyright (C) 1999-2003 Nemosoft Unv. * Copyright (C) 2004-2006 Luc Saillard * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * 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 */ #ifndef PWC_DEC1_H #define PWC_DEC1_H void pwc_dec1_init(int type, int release, void *buffer, void *private_data); void pwc_dec1_exit(void); #endif pwcbsd/pwcbsd/pwc-dec23.c000644 000423 000000 00000040317 10546676137 015737 0ustar00luigiwheel000000 000000 /* * PWCBSD - Philips USB webcam driver for FreeBSD 5.4 and higher * * Copyright (C) 2006 Raaf * * Based on the Linux pwc driver. * * Copyright (C) 1999-2003 Nemosoft Unv. * Copyright (C) 2004-2006 Luc Saillard * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * 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 "pwc.h" #include "pwc-timon.h" #include "pwc-kiara.h" #include "pwc-dec23.h" #include "pwc-ioctl.h" static void fill_table_a000(unsigned int *p) { static unsigned int initial_values[12] = { 0xFFAD9B00, 0xFFDDEE00, 0x00221200, 0x00526500, 0xFFC21E00, 0x003DE200, 0xFF924B80, 0xFFD2A300, 0x002D5D00, 0x006DB480, 0xFFED3E00, 0x0012C200 }; static unsigned int values_derivated[12] = { 0x0000A4CA, 0x00004424, 0xFFFFBBDC, 0xFFFF5B36, 0x00007BC4, 0xFFFF843C, 0x0000DB69, 0x00005ABA, 0xFFFFA546, 0xFFFF2497, 0x00002584, 0xFFFFDA7C }; unsigned int temp_values[12]; int i, j; memcpy(temp_values, initial_values, sizeof(initial_values)); for (i = 0; i < 256; i++) { for (j = 0; j < 12; j++) { *p++ = temp_values[j]; temp_values[j] += values_derivated[j]; } } } static void fill_table_d000(unsigned char *p) { int bit, byte; for (bit = 0; bit < 8; bit++) { unsigned char bitpower = 1 << bit; unsigned char mask = bitpower - 1; for (byte = 0; byte < 256; byte++) { if (byte & bitpower) *p++ = -(byte & mask); else *p++ = (byte & mask); } } } /* * * Kiara: 0 <= ver <= 7 * Timon: 0 <= ver <= 15 * */ static void fill_table_color(unsigned int version, const unsigned int *romtable, unsigned char *p0004, unsigned char *p8004) { const unsigned int *table; unsigned char *p0, *p8; int i, j, k; int dl, bit, pw; romtable += version * 256; for (i = 0; i < 2; i++) { table = romtable + i * 128; for (dl = 0; dl < 16; dl++) { p0 = p0004 + (i << 14) + (dl << 10); p8 = p8004 + (i << 12) + (dl << 8); for (j = 0; j < 8; j++, table++, p0 += 128) { for (k = 0; k < 16; k++) { if (k == 0) bit = 1; else if (k >= 1 && k < 3) bit = (table[0] >> 15) & 7; else if (k >= 3 && k < 6) bit = (table[0] >> 12) & 7; else if (k >= 6 && k < 10) bit = (table[0] >> 9) & 7; else if (k >= 10 && k < 13) bit = (table[0] >> 6) & 7; else if (k >= 13 && k < 15) bit = (table[0] >> 3) & 7; else bit = (table[0]) & 7; if (k == 0) *(unsigned char *)p8++ = 8; else *(unsigned char *)p8++ = j - bit; *(unsigned char *)p8++ = bit; pw = 1 << bit; p0[k + 0x00] = (1 * pw) + 0x80; p0[k + 0x10] = (2 * pw) + 0x80; p0[k + 0x20] = (3 * pw) + 0x80; p0[k + 0x30] = (4 * pw) + 0x80; p0[k + 0x40] = (-pw) + 0x80; p0[k + 0x50] = (2 * -pw) + 0x80; p0[k + 0x60] = (3 * -pw) + 0x80; p0[k + 0x70] = (4 * -pw) + 0x80; } /* end of for (k=0; k<16; k++, p8++) */ } /* end of for (j=0; j<8; j++ , table++) */ } /* end of for (dl=0; dl<16; dl++) */ } /* end of for (i=0; i<2; i++) */ } /* * precision = (pdev->xx + pdev->yy) * */ static void fill_table_dc00_d800(unsigned int precision, unsigned int *pdc00, unsigned int *pd800) { int i; unsigned int offset1, offset2; for (i = 0, offset1 = 0x4000, offset2 = 0; i < 256; i++, offset1 += 0x7BC4, offset2 += 0x7BC4) { unsigned int msb = offset1 >> 15; if (msb > 255) { if (msb) msb = 0; else msb = 255; } *pdc00++ = msb << precision; *pd800++ = offset2; } } /* * struct { * unsigned char op; // operation to execute * unsigned char bits; // bits use to perform operation * unsigned char offset1; // offset to add to access in the table_0004 % 16 * unsigned char offset2; // offset to add to access in the table_0004 * } * */ static unsigned int table_ops[64*4] = { 0x02, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x04, 0x01, 0x10, 0x00, 0x06, 0x01, 0x30, 0x02, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x40, 0x00, 0x05, 0x01, 0x20, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x04, 0x01, 0x50, 0x00, 0x05, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x40, 0x00, 0x05, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x04, 0x01, 0x10, 0x00, 0x06, 0x02, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x40, 0x00, 0x05, 0x01, 0x60, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x04, 0x01, 0x50, 0x00, 0x05, 0x02, 0x40, 0x02, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x40, 0x00, 0x05, 0x03, 0x40, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x04, 0x01, 0x10, 0x00, 0x06, 0x01, 0x70, 0x02, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x40, 0x00, 0x05, 0x01, 0x20, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x04, 0x01, 0x50, 0x00, 0x05, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x40, 0x00, 0x05, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x04, 0x01, 0x10, 0x00, 0x06, 0x02, 0x50, 0x02, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x40, 0x00, 0x05, 0x01, 0x60, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x04, 0x01, 0x50, 0x00, 0x05, 0x02, 0x40, 0x02, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x40, 0x00, 0x05, 0x03, 0x40, 0x01, 0x00, 0x00, 0x00 }; /* * TODO: multiply by 4 all values * */ static unsigned int MulIdx[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 6, 7, 8, 9, 7, 10, 11, 8, 8, 11, 10, 7, 9, 8, 7, 6, 4, 5, 5, 4, 4, 5, 5, 4, 4, 5, 5, 4, 4, 5, 5, 4, 1, 3, 0, 2, 1, 3, 0, 2, 1, 3, 0, 2, 1, 3, 0, 2, 0, 3, 3, 0, 1, 2, 2, 1, 2, 1, 1, 2, 3, 0, 0, 3, 0, 1, 2, 3, 3, 2, 1, 0, 3, 2, 1, 0, 0, 1, 2, 3, 1, 1, 1, 1, 3, 3, 3, 3, 0, 0, 0, 0, 2, 2, 2, 2, 7, 10, 11, 8, 9, 8, 7, 6, 6, 7, 8, 9, 8, 11, 10, 7, 4, 5, 5, 4, 5, 4, 4, 5, 5, 4, 4, 5, 4, 5, 5, 4, 7, 9, 6, 8, 10, 8, 7, 11, 11, 7, 8, 10, 8, 6, 9, 7, 1, 3, 0, 2, 2, 0, 3, 1, 2, 0, 3, 1, 1, 3, 0, 2, 1, 2, 2, 1, 3, 0, 0, 3, 0, 3, 3, 0, 2, 1, 1, 2, 10, 8, 7, 11, 8, 6, 9, 7, 7, 9, 6, 8, 11, 7, 8, 10 }; void pwc_dec23_init(int type, int release, unsigned char *mode, void *data) { int flags; struct pwc_dec23_private *pdev = data; release = release; switch (type) { case 720: case 730: case 740: case 750: flags = mode[2] & 0x18; /* our: flags = 8, mode[2]==e8 */ if (flags == 8) pdev->zz = 7; else if (flags == 0x10) pdev->zz = 8; else pdev->zz = 6; flags = mode[2] >> 5; /* our: 7 */ fill_table_color(flags, (unsigned int *)KiaraRomTable, pdev->table_0004, pdev->table_8004); break; case 675: case 680: case 690: flags = mode[2] & 6; if (flags == 2) pdev->zz = 7; else if (flags == 4) pdev->zz = 8; else pdev->zz = 6; flags = mode[2] >> 3; fill_table_color(flags, (unsigned int *)TimonRomTable, pdev->table_0004, pdev->table_8004); break; default: /* Not supported */ return; } /* * * * ** */ pdev->xx = 8 - pdev->zz; pdev->yy = 15 - pdev->xx; pdev->zzmask = 0xFF >> pdev->xx; //pdev->zzmask = (1U<zz)-1; fill_table_dc00_d800(pdev->xx + pdev->yy, pdev->table_dc00, pdev->table_d800); fill_table_a000(pdev->table_a004); fill_table_d000(pdev->table_d004); } /* * To manage the stream, we keep in a 32 bits variables, * the next bits in the stream. fill_reservoir() add to * the reservoir at least wanted nbits. * */ #define fill_nbits(reservoir,nbits_in_reservoir,stream,nbits_wanted) do { \ while (nbits_in_reservoir>= nbits_wanted; \ nbits_in_reservoir -= nbits_wanted; \ } while(0); static void DecompressBand23(const struct pwc_dec23_private *pdev, const unsigned char *rawyuv, unsigned char *planar_y, unsigned char *planar_u, unsigned char *planar_v, unsigned int image_x, /* aka number of pixels wanted ??? */ unsigned int pixels_per_line, /* aka number of pixels per line */ int flags) { unsigned int reservoir, nbits_in_reservoir; int first_4_bits; unsigned int bytes_per_channel; int line_size; /* size of the line (4Y+U+V) */ int passes; const unsigned char *ptable0004, *ptable8004; int even_line; unsigned int temp_colors[16]; int nblocks; const unsigned char *stream; unsigned char *dest_y, *dest_u = NULL, *dest_v = NULL; unsigned int offset_to_plane_u, offset_to_plane_v; int i; reservoir = 0; nbits_in_reservoir = 0; stream = rawyuv + 1; /* The first byte of the stream is skipped */ even_line = 1; get_nbits(reservoir, nbits_in_reservoir, stream, 4, first_4_bits); line_size = pixels_per_line * 3; for (passes = 0; passes < 2; passes++) { if (passes == 0) { bytes_per_channel = pixels_per_line; dest_y = planar_y; nblocks = image_x / 4; } else { /* Format planar: All Y, then all U, then all V */ bytes_per_channel = pixels_per_line / 2; dest_u = planar_u; dest_v = planar_v; dest_y = dest_u; nblocks = image_x / 8; } offset_to_plane_u = bytes_per_channel * 2; offset_to_plane_v = bytes_per_channel * 3; /* printk(KERN_DEBUG "bytes_per_channel = %d\n",bytes_per_channel); printk(KERN_DEBUG "offset_to_plane_u = %d\n",offset_to_plane_u); printk(KERN_DEBUG "offset_to_plane_v = %d\n",offset_to_plane_v); */ while (nblocks-- > 0) { unsigned int gray_index; fill_nbits(reservoir, nbits_in_reservoir, stream, 16); gray_index = reservoir & pdev->zzmask; reservoir >>= pdev->zz; nbits_in_reservoir -= pdev->zz; fill_nbits(reservoir, nbits_in_reservoir, stream, 2); if ((reservoir & 3) == 0) { reservoir >>= 2; nbits_in_reservoir -= 2; for (i = 0; i < 16; i++) temp_colors[i] = pdev->table_dc00[gray_index]; } else { unsigned int channel_v, offset1; /* swap bit 0 and 2 of offset_OR */ channel_v = ((reservoir & 1) << 2) | (reservoir & 2) | ((reservoir & 4) >> 2); reservoir >>= 3; nbits_in_reservoir -= 3; for (i = 0; i < 16; i++) temp_colors[i] = pdev->table_d800[gray_index]; ptable0004 = pdev->table_0004 + (passes * 16384) + (first_4_bits * 1024) + (channel_v * 128); ptable8004 = pdev->table_8004 + (passes * 4096) + (first_4_bits * 256) + (channel_v * 32); offset1 = 0; while (1) { unsigned int index_in_table_ops, op, rows = 0; fill_nbits(reservoir, nbits_in_reservoir, stream, 16); /* mode is 0,1 or 2 */ index_in_table_ops = (reservoir & 0x3F); op = table_ops[index_in_table_ops * 4]; if (op == 2) { reservoir >>= 2; nbits_in_reservoir -= 2; break; /* exit the while(1) */ } if (op == 0) { unsigned int shift; offset1 = (offset1 + table_ops [index_in_table_ops * 4 + 2]) & 0x0F; shift = table_ops[index_in_table_ops * 4 + 1]; reservoir >>= shift; nbits_in_reservoir -= shift; rows = ptable0004[offset1 + table_ops [index_in_table_ops * 4 + 3]]; } if (op == 1) { /* 10bits [ xxxx xxxx yyyy 000 ] * yyy => offset in the table8004 * xxx => offset in the tabled004 */ unsigned int mask, shift; unsigned int col1, row1, total_bits; offset1 = (offset1 + ((reservoir >> 3) & 0x0F) + 1) & 0x0F; col1 = (reservoir >> 7) & 0xFF; row1 = ptable8004[offset1 * 2]; /* Bit mask table */ mask = pdev-> table_d004[(row1 << 8) + col1]; shift = ptable8004[offset1 * 2 + 1]; rows = ((mask << shift) + 0x80) & 0xFF; total_bits = row1 + 8; reservoir >>= total_bits; nbits_in_reservoir -= total_bits; } { const unsigned int *table_a004 = pdev->table_a004 + rows * 12; unsigned int *poffset = MulIdx + offset1 * 16; /* 64/4 (int) */ for (i = 0; i < 16; i++) { temp_colors[i] += table_a004 [*poffset]; poffset++; } } } } #define USE_SIGNED_INT_FOR_COLOR #ifdef USE_SIGNED_INT_FOR_COLOR # define CLAMP(x) ((x)>255?255:((x)<0?0:x)) #else # define CLAMP(x) ((x)>255?255:x) #endif if (passes == 0) { #ifdef USE_SIGNED_INT_FOR_COLOR const int *c = temp_colors; #else const unsigned int *c = temp_colors; #endif unsigned char *d; d = dest_y; for (i = 0; i < 4; i++, c++) *d++ = CLAMP((*c) >> pdev->yy); d = dest_y + bytes_per_channel; for (i = 0; i < 4; i++, c++) *d++ = CLAMP((*c) >> pdev->yy); d = dest_y + offset_to_plane_u; for (i = 0; i < 4; i++, c++) *d++ = CLAMP((*c) >> pdev->yy); d = dest_y + offset_to_plane_v; for (i = 0; i < 4; i++, c++) *d++ = CLAMP((*c) >> pdev->yy); dest_y += 4; } else if (passes == 1) { #ifdef USE_SIGNED_INT_FOR_COLOR int *c1 = temp_colors; int *c2 = temp_colors + 4; #else unsigned int *c1 = temp_colors; unsigned int *c2 = temp_colors + 4; #endif unsigned char *d; d = dest_y; for (i = 0; i < 4; i++, c1++, c2++) { *d++ = CLAMP((*c1) >> pdev->yy); *d++ = CLAMP((*c2) >> pdev->yy); } c1 = temp_colors + 12; //c2 = temp_colors+8; d = dest_y + bytes_per_channel; for (i = 0; i < 4; i++, c1++, c2++) { *d++ = CLAMP((*c1) >> pdev->yy); *d++ = CLAMP((*c2) >> pdev->yy); } if (even_line) { /* Each line, swap u/v */ even_line = 0; dest_y = dest_v; dest_u += 8; } else { even_line = 1; dest_y = dest_u; dest_v += 8; } } } /* end of while (nblocks-->0) */ } /* end of for (passes=0;passes<2;passes++) */ } /** * * image: size of the image wanted * view : size of the image returned by the camera * offset: (x,y) to displayer image in the view * * src: raw data * dst: image output * flags: PWCX_FLAG_PLANAR * pdev: private buffer * bandlength: * */ void pwc_dec23_decompress(const struct pwc_coord *image, const struct pwc_coord *view, const struct pwc_coord *offset, const char*src, char *dst, int flags, const void *data, int bandlength) { const struct pwc_dec23_private *pdev = data; unsigned char *pout, *pout_planar_y = NULL, *pout_planar_u = NULL, *pout_planar_v = NULL; int i, n, stride, pixel_size; if (flags & PWCX_FLAG_BAYER) { pout = dst + (view->x * offset->y) + offset->x; pixel_size = view->x * 4; } else { n = view->x * view->y; /* offset in Y plane */ stride = view->x * offset->y; pout_planar_y = dst + stride + offset->x; /* offsets in U/V planes */ stride = (view->x * offset->y) / 4 + offset->x / 2; pout_planar_u = dst + n + +stride; pout_planar_v = dst + n + n / 4 + stride; pixel_size = view->x * 4; } for (i = 0; i < image->y; i += 4) { if (flags & PWCX_FLAG_BAYER) { //TODO: //DecompressBandBayer(pdev,src,pout,image.x,view->x,flags); src += bandlength; pout += pixel_size; } else { DecompressBand23(pdev, src, pout_planar_y, pout_planar_u, pout_planar_v, image->x, view->x, flags); src += bandlength; pout_planar_y += pixel_size; pout_planar_u += view->x; pout_planar_v += view->x; } } } void pwc_dec23_exit(void) { /* Do nothing */ } pwcbsd/pwcbsd/pwc-dec23.h000644 000423 000000 00000003202 10546676137 015734 0ustar00luigiwheel000000 000000 /* * PWCBSD - Philips USB webcam driver for FreeBSD 5.4 and higher * * Copyright (C) 2006 Raaf * * Based on the Linux pwc driver. * * Copyright (C) 1999-2003 Nemosoft Unv. * Copyright (C) 2004-2006 Luc Saillard * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * 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 */ #ifndef PWC_DEC23_H #define PWC_DEC23_H struct pwc_dec23_private { unsigned char xx,yy,zz,zzmask; unsigned char table_0004[2*0x4000]; unsigned char table_8004[2*0x1000]; unsigned int table_a004[256*12]; unsigned char table_d004[8*256]; unsigned int table_d800[256]; unsigned int table_dc00[256]; }; void pwc_dec23_init(int type, int release, unsigned char *buffer, void *private_data); void pwc_dec23_exit(void); void pwc_dec23_decompress(const struct pwc_coord *image, const struct pwc_coord *view, const struct pwc_coord *offset, const char *src, char *dst, int flags, const void *data, int bandlength); #endif pwcbsd/pwcbsd/pwc-ioctl.h000644 000423 000000 00000022270 10552633465 016146 0ustar00luigiwheel000000 000000 /* * PWCBSD - Philips USB webcam driver for FreeBSD 5.4 and higher * * Copyright (C) 2006 Raaf * * Based on the Linux pwc driver. * * Copyright (C) 1999-2003 Nemosoft Unv. * Copyright (C) 2004-2006 Luc Saillard * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * 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 */ /* This is pwc-ioctl.h It contains structures and defines to communicate from user space directly to the driver. */ #ifndef PWC_IOCTL_H #define PWC_IOCTL_H /* Enumeration of image sizes */ enum { PSZ_SQCIF, /* 120x96 */ PSZ_QSIF, /* 160x120 */ PSZ_QCIF, /* 176x144 */ PSZ_QPAL, /* 192x144 */ PSZ_SIF, /* 320x240 */ PSZ_CIF, /* 352x288 */ PSZ_PAL, /* 384x288 */ PSZ_VGA, /* 640x480 */ PSZ_XGA, /* 1024x768 */ PSZ_MAX }; /* The frame rate is encoded in the video_window.flags parameter using the upper 16 bits, since some flags are defined nowadays. The following defines provide a mask and shift to filter out this value. This value can also be passing using the private flag when using v4l2 and VIDIOC_S_FMT ioctl. In 'Snapshot' mode the camera freezes its automatic exposure and colour balance controls. */ #define PWC_FPS_SHIFT 16 #define PWC_FPS_MASK 0x00FF0000 #define PWC_FPS_FRMASK 0x003F0000 #define PWC_FPS_SNAPSHOT 0x00400000 #define PWC_QLT_MASK 0x03000000 #define PWC_QLT_SHIFT 24 /* structure for transfering x & y coordinates */ struct pwc_coord { int x, y; /* guess what */ int size; /* size, or offset */ }; /* Used with VIDIOCPWCPROBE */ struct pwc_probe { char name[32]; int type; }; struct pwc_serial { char serial[30]; /* String with serial number. Contains terminating 0 */ }; /* pwc_whitebalance.mode values */ #define PWC_WB_INDOOR 0 #define PWC_WB_OUTDOOR 1 #define PWC_WB_FL 2 #define PWC_WB_MANUAL 3 #define PWC_WB_AUTO 4 /* Used with VIDIOCPWC[SG]AWB (Auto White Balance). Set mode to one of the PWC_WB_* values above. *red and *blue are the respective gains of these colour components inside the camera; range 0..65535 When 'mode' == PWC_WB_MANUAL, 'manual_red' and 'manual_blue' are set or read; otherwise undefined. 'read_red' and 'read_blue' are read-only. */ struct pwc_whitebalance { int mode; int manual_red, manual_blue; /* R/W */ int read_red, read_blue; /* R/O */ }; /* 'control_speed' and 'control_delay' are used in automatic whitebalance mode, and tell the camera how fast it should react to changes in lighting, and with how much delay. Valid values are 0..65535. */ struct pwc_wb_speed { int control_speed; int control_delay; }; /* Used with VIDIOCPWC[SG]LED */ struct pwc_leds { int led_on; /* Led on-time; range = 0..25000 */ int led_off; /* Led off-time; range = 0..25000 */ }; /* Image size (used with GREALSIZE) */ struct pwc_imagesize { int width; int height; }; /* Defines and structures for Motorized Pan & Tilt */ #define PWC_MPT_PAN 0x01 #define PWC_MPT_TILT 0x02 #define PWC_MPT_TIMEOUT 0x04 /* for status */ /* Set angles; when absolute != 0, the angle is absolute and the driver calculates the relative offset for you. This can only be used with VIDIOCPWCSANGLE; VIDIOCPWCGANGLE always returns absolute angles. */ struct pwc_mpt_angles { int absolute; /* write-only */ int pan; /* degrees * 100 */ int tilt; /* degress * 100 */ }; /* Range of angles of the camera, both horizontally and vertically. */ struct pwc_mpt_range { int pan_min, pan_max; /* degrees * 100 */ int tilt_min, tilt_max; }; struct pwc_mpt_status { int status; int time_pan; int time_tilt; }; /* This is used for out-of-kernel decompression. With it, you can get all the necessary information to initialize and use the decompressor routines in standalone applications. */ struct pwc_video_command { int type; /* camera type (645, 675, 730, etc.) */ int release; /* release number */ int size; /* one of PSZ_* */ int alternate; int command_len; /* length of USB video command */ unsigned char command_buf[13]; /* Actual USB video command */ int bandlength; /* >0 = compressed */ int frame_size; /* Size of one (un)compressed frame */ }; /* Flags for PWCX subroutines. Not all modules honour all flags. */ #define PWCX_FLAG_PLANAR 0x0001 #define PWCX_FLAG_BAYER 0x0008 /* IOCTL definitions */ /* Restore user settings */ #define VIDIOCPWCRUSER _IO('v', 192) /* Save user settings */ #define VIDIOCPWCSUSER _IO('v', 193) /* Restore factory settings */ #define VIDIOCPWCFACTORY _IO('v', 194) /* You can manipulate the compression factor. A compression preference of 0 means use uncompressed modes when available; 1 is low compression, 2 is medium and 3 is high compression preferred. Of course, the higher the compression, the lower the bandwidth used but more chance of artefacts in the image. The driver automatically chooses a higher compression when the preferred mode is not available. */ /* Set preferred compression quality (0 = uncompressed, 3 = highest compression) */ #define VIDIOCPWCSCQUAL _IOW('v', 195, int) /* Get preferred compression quality */ #define VIDIOCPWCGCQUAL _IOR('v', 195, int) /* Retrieve serial number of camera */ #define VIDIOCPWCGSERIAL _IOR('v', 198, struct pwc_serial) /* This is a probe function; since so many devices are supported, it becomes difficult to include all the names in programs that want to check for the enhanced Philips stuff. So in stead, try this PROBE; it returns a structure with the original name, and the corresponding Philips type. To use, fill the structure with zeroes, call PROBE and if that succeeds, compare the name with that returned from VIDIOCGCAP; they should be the same. If so, you can be assured it is a Philips (OEM) cam and the type is valid. */ #define VIDIOCPWCPROBE _IOR('v', 199, struct pwc_probe) /* Set AGC (Automatic Gain Control); int < 0 = auto, 0..65535 = fixed */ #define VIDIOCPWCSAGC _IOW('v', 200, int) /* Get AGC; int < 0 = auto; >= 0 = fixed, range 0..65535 */ #define VIDIOCPWCGAGC _IOR('v', 200, int) /* Set shutter speed; int < 0 = auto; >= 0 = fixed, range 0..65535 */ #define VIDIOCPWCSSHUTTER _IOW('v', 201, int) /* Get shutter speed; int < 0 = auto; >= 0 = fixed, range 0..65535 */ #define VIDIOCPWCGSHUTTER _IOR('v', 201, int) /* Color compensation (Auto White Balance) */ #define VIDIOCPWCSAWB _IOW('v', 202, struct pwc_whitebalance) #define VIDIOCPWCGAWB _IOR('v', 202, struct pwc_whitebalance) /* Auto WB speed */ #define VIDIOCPWCSAWBSPEED _IOW('v', 203, struct pwc_wb_speed) #define VIDIOCPWCGAWBSPEED _IOR('v', 203, struct pwc_wb_speed) /* LEDs on/off/blink; int range 0..65535 */ #define VIDIOCPWCSLED _IOW('v', 205, struct pwc_leds) #define VIDIOCPWCGLED _IOR('v', 205, struct pwc_leds) /* Contour (sharpness); int < 0 = auto, 0..65536 = fixed */ #define VIDIOCPWCSCONTOUR _IOW('v', 206, int) #define VIDIOCPWCGCONTOUR _IOR('v', 206, int) /* Backlight compensation; 0 = off, otherwise on */ #define VIDIOCPWCSBACKLIGHT _IOW('v', 207, int) #define VIDIOCPWCGBACKLIGHT _IOR('v', 207, int) /* Flickerless mode; = 0 off, otherwise on */ #define VIDIOCPWCSFLICKER _IOW('v', 208, int) #define VIDIOCPWCGFLICKER _IOR('v', 208, int) /* Dynamic noise reduction; 0 off, 3 = high noise reduction */ #define VIDIOCPWCSDYNNOISE _IOW('v', 209, int) #define VIDIOCPWCGDYNNOISE _IOR('v', 209, int) /* Real image size as used by the camera; tells you whether or not there's a gray border around the image */ #define VIDIOCPWCGREALSIZE _IOR('v', 210, struct pwc_imagesize) /* Motorized pan & tilt functions */ #define VIDIOCPWCMPTRESET _IOW('v', 211, int) #define VIDIOCPWCMPTGRANGE _IOR('v', 211, struct pwc_mpt_range) #define VIDIOCPWCMPTSANGLE _IOW('v', 212, struct pwc_mpt_angles) #define VIDIOCPWCMPTGANGLE _IOR('v', 212, struct pwc_mpt_angles) #define VIDIOCPWCMPTSTATUS _IOR('v', 213, struct pwc_mpt_status) /* Get the USB set-video command; needed for initializing libpwcx */ #define VIDIOCPWCGVIDCMD _IOR('v', 215, struct pwc_video_command) struct pwc_table_init_buffer { int len; char *buffer; }; #define VIDIOCPWCGVIDTABLE _IOR('v', 216, struct pwc_table_init_buffer) /* Colour mode ; 0 = off, 1 = on */ #define VIDIOCPWCSCOLOUR _IOW('v', 217, int) #define VIDIOCPWCGCOLOUR _IOR('v', 217, int) /* * This is private command used when communicating with v4l2. * In the future all private ioctl will be remove/replace to * use interface offer by v4l2. */ #define V4L2_CID_PRIVATE_SAVE_USER (V4L2_CID_PRIVATE_BASE + 0) #define V4L2_CID_PRIVATE_RESTORE_USER (V4L2_CID_PRIVATE_BASE + 1) #define V4L2_CID_PRIVATE_RESTORE_FACTORY (V4L2_CID_PRIVATE_BASE + 2) #endif pwcbsd/pwcbsd/pwc-kiara.c000644 000423 000000 00000106442 10546676137 016130 0ustar00luigiwheel000000 000000 /* * PWCBSD - Philips USB webcam driver for FreeBSD 5.4 and higher * * Copyright (C) 2006 Raaf * * Based on the Linux pwc driver. * * Copyright (C) 1999-2003 Nemosoft Unv. * Copyright (C) 2004-2006 Luc Saillard * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * 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 */ /* This tables contains entries for the 730/740/750 (Kiara) camera, with 4 different qualities (no compression, low, medium, high). It lists the bandwidth requirements for said mode by its alternate interface number. An alternate of 0 means that the mode is unavailable. There are 6 * 4 * 4 entries: 6 different resolutions subqcif, qsif, qcif, sif, cif, vga 6 framerates: 5, 10, 15, 20, 25, 30 4 compression modi: none, low, medium, high When an uncompressed mode is not available, the next available compressed mode will be chosen (unless the decompressor is absent). Sometimes there are only 1 or 2 compressed modes available; in that case entries are duplicated. */ #include "pwc-kiara.h" #include "pwc-uncompress.h" const struct Kiara_table_entry Kiara_table[PSZ_MAX][6][4] = { /* SQCIF */ { /* 5 fps */ { {0, }, {0, }, {0, }, {0, }, }, /* 10 fps */ { {0, }, {0, }, {0, }, {0, }, }, /* 15 fps */ { {0, }, {0, }, {0, }, {0, }, }, /* 20 fps */ { {0, }, {0, }, {0, }, {0, }, }, /* 25 fps */ { {0, }, {0, }, {0, }, {0, }, }, /* 30 fps */ { {0, }, {0, }, {0, }, {0, }, }, }, /* QSIF */ { /* 5 fps */ { {1, 146, 0, {0x1D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0x00, 0x80}}, {1, 146, 0, {0x1D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0x00, 0x80}}, {1, 146, 0, {0x1D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0x00, 0x80}}, {1, 146, 0, {0x1D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0x00, 0x80}}, }, /* 10 fps */ { {2, 291, 0, {0x1C, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x23, 0x01, 0x80}}, {1, 192, 630, {0x14, 0xF4, 0x30, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xC0, 0x00, 0x80}}, {1, 192, 630, {0x14, 0xF4, 0x30, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xC0, 0x00, 0x80}}, {1, 192, 630, {0x14, 0xF4, 0x30, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xC0, 0x00, 0x80}}, }, /* 15 fps */ { {3, 437, 0, {0x1B, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0xB5, 0x01, 0x80}}, {2, 292, 640, {0x13, 0xF4, 0x30, 0x13, 0xF7, 0x13, 0x2F, 0x13, 0x20, 0x24, 0x01, 0x80}}, {2, 292, 640, {0x13, 0xF4, 0x30, 0x13, 0xF7, 0x13, 0x2F, 0x13, 0x20, 0x24, 0x01, 0x80}}, {1, 192, 420, {0x13, 0xF4, 0x30, 0x0D, 0x1B, 0x0C, 0x53, 0x1E, 0x18, 0xC0, 0x00, 0x80}}, }, /* 20 fps */ { {4, 589, 0, {0x1A, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x4D, 0x02, 0x80}}, {3, 448, 730, {0x12, 0xF4, 0x30, 0x16, 0xC9, 0x16, 0x01, 0x0E, 0x18, 0xC0, 0x01, 0x80}}, {2, 292, 476, {0x12, 0xF4, 0x30, 0x0E, 0xD8, 0x0E, 0x10, 0x19, 0x18, 0x24, 0x01, 0x80}}, {1, 192, 312, {0x12, 0xF4, 0x50, 0x09, 0xB3, 0x08, 0xEB, 0x1E, 0x18, 0xC0, 0x00, 0x80}}, }, /* 25 fps */ { {5, 703, 0, {0x19, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0xBF, 0x02, 0x80}}, {3, 447, 610, {0x11, 0xF4, 0x30, 0x13, 0x0B, 0x12, 0x43, 0x14, 0x28, 0xBF, 0x01, 0x80}}, {2, 292, 398, {0x11, 0xF4, 0x50, 0x0C, 0x6C, 0x0B, 0xA4, 0x1E, 0x28, 0x24, 0x01, 0x80}}, {1, 193, 262, {0x11, 0xF4, 0x50, 0x08, 0x23, 0x07, 0x5B, 0x1E, 0x28, 0xC1, 0x00, 0x80}}, }, /* 30 fps */ { {8, 874, 0, {0x18, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x6A, 0x03, 0x80}}, {5, 704, 730, {0x10, 0xF4, 0x30, 0x16, 0xC9, 0x16, 0x01, 0x0E, 0x28, 0xC0, 0x02, 0x80}}, {3, 448, 492, {0x10, 0xF4, 0x30, 0x0F, 0x5D, 0x0E, 0x95, 0x15, 0x28, 0xC0, 0x01, 0x80}}, {2, 292, 320, {0x10, 0xF4, 0x50, 0x09, 0xFB, 0x09, 0x33, 0x1E, 0x28, 0x24, 0x01, 0x80}}, }, }, /* QCIF */ { /* 5 fps */ { {0, }, {0, }, {0, }, {0, }, }, /* 10 fps */ { {0, }, {0, }, {0, }, {0, }, }, /* 15 fps */ { {0, }, {0, }, {0, }, {0, }, }, /* 20 fps */ { {0, }, {0, }, {0, }, {0, }, }, /* 25 fps */ { {0, }, {0, }, {0, }, {0, }, }, /* 30 fps */ { {0, }, {0, }, {0, }, {0, }, }, }, /* SIF */ { /* 5 fps */ { {4, 582, 0, {0x0D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x46, 0x02, 0x80}}, {3, 387, 1276, {0x05, 0xF4, 0x30, 0x27, 0xD8, 0x26, 0x48, 0x03, 0x10, 0x83, 0x01, 0x80}}, {2, 291, 960, {0x05, 0xF4, 0x30, 0x1D, 0xF2, 0x1C, 0x62, 0x04, 0x10, 0x23, 0x01, 0x80}}, {1, 191, 630, {0x05, 0xF4, 0x50, 0x13, 0xA9, 0x12, 0x19, 0x05, 0x18, 0xBF, 0x00, 0x80}}, }, /* 10 fps */ { {0, }, {6, 775, 1278, {0x04, 0xF4, 0x30, 0x27, 0xE8, 0x26, 0x58, 0x05, 0x30, 0x07, 0x03, 0x80}}, {3, 447, 736, {0x04, 0xF4, 0x30, 0x16, 0xFB, 0x15, 0x6B, 0x05, 0x28, 0xBF, 0x01, 0x80}}, {2, 292, 480, {0x04, 0xF4, 0x70, 0x0E, 0xF9, 0x0D, 0x69, 0x09, 0x28, 0x24, 0x01, 0x80}}, }, /* 15 fps */ { {0, }, {9, 955, 1050, {0x03, 0xF4, 0x30, 0x20, 0xCF, 0x1F, 0x3F, 0x06, 0x48, 0xBB, 0x03, 0x80}}, {4, 592, 650, {0x03, 0xF4, 0x30, 0x14, 0x44, 0x12, 0xB4, 0x08, 0x30, 0x50, 0x02, 0x80}}, {3, 448, 492, {0x03, 0xF4, 0x50, 0x0F, 0x52, 0x0D, 0xC2, 0x09, 0x38, 0xC0, 0x01, 0x80}}, }, /* 20 fps */ { {0, }, {9, 958, 782, {0x02, 0xF4, 0x30, 0x18, 0x6A, 0x16, 0xDA, 0x0B, 0x58, 0xBE, 0x03, 0x80}}, {5, 703, 574, {0x02, 0xF4, 0x50, 0x11, 0xE7, 0x10, 0x57, 0x0B, 0x40, 0xBF, 0x02, 0x80}}, {3, 446, 364, {0x02, 0xF4, 0x90, 0x0B, 0x5C, 0x09, 0xCC, 0x0E, 0x38, 0xBE, 0x01, 0x80}}, }, /* 25 fps */ { {0, }, {9, 958, 654, {0x01, 0xF4, 0x30, 0x14, 0x66, 0x12, 0xD6, 0x0B, 0x50, 0xBE, 0x03, 0x80}}, {6, 776, 530, {0x01, 0xF4, 0x50, 0x10, 0x8C, 0x0E, 0xFC, 0x0C, 0x48, 0x08, 0x03, 0x80}}, {4, 592, 404, {0x01, 0xF4, 0x70, 0x0C, 0x96, 0x0B, 0x06, 0x0B, 0x48, 0x50, 0x02, 0x80}}, }, /* 30 fps */ { {0, }, {9, 957, 526, {0x00, 0xF4, 0x50, 0x10, 0x68, 0x0E, 0xD8, 0x0D, 0x58, 0xBD, 0x03, 0x80}}, {6, 775, 426, {0x00, 0xF4, 0x70, 0x0D, 0x48, 0x0B, 0xB8, 0x0F, 0x50, 0x07, 0x03, 0x80}}, {4, 590, 324, {0x00, 0x7A, 0x88, 0x0A, 0x1C, 0x08, 0xB4, 0x0E, 0x50, 0x4E, 0x02, 0x80}}, }, }, /* CIF */ { /* 5 fps */ { {0, }, {0, }, {0, }, {0, }, }, /* 10 fps */ { {0, }, {0, }, {0, }, {0, }, }, /* 15 fps */ { {0, }, {0, }, {0, }, {0, }, }, /* 20 fps */ { {0, }, {0, }, {0, }, {0, }, }, /* 25 fps */ { {0, }, {0, }, {0, }, {0, }, }, /* 30 fps */ { {0, }, {0, }, {0, }, {0, }, }, }, /* VGA */ { /* 5 fps */ { {0, }, {6, 773, 1272, {0x25, 0xF4, 0x30, 0x27, 0xB6, 0x24, 0x96, 0x02, 0x30, 0x05, 0x03, 0x80}}, {4, 592, 976, {0x25, 0xF4, 0x50, 0x1E, 0x78, 0x1B, 0x58, 0x03, 0x30, 0x50, 0x02, 0x80}}, {3, 448, 738, {0x25, 0xF4, 0x90, 0x17, 0x0C, 0x13, 0xEC, 0x04, 0x30, 0xC0, 0x01, 0x80}}, }, /* 10 fps */ { {0, }, {9, 956, 788, {0x24, 0xF4, 0x70, 0x18, 0x9C, 0x15, 0x7C, 0x03, 0x48, 0xBC, 0x03, 0x80}}, {6, 776, 640, {0x24, 0xF4, 0xB0, 0x13, 0xFC, 0x11, 0x2C, 0x04, 0x48, 0x08, 0x03, 0x80}}, {4, 592, 488, {0x24, 0x7A, 0xE8, 0x0F, 0x3C, 0x0C, 0x6C, 0x06, 0x48, 0x50, 0x02, 0x80}}, }, /* 15 fps */ { {0, }, {9, 957, 526, {0x23, 0x7A, 0xE8, 0x10, 0x68, 0x0D, 0x98, 0x06, 0x58, 0xBD, 0x03, 0x80}}, {9, 957, 526, {0x23, 0x7A, 0xE8, 0x10, 0x68, 0x0D, 0x98, 0x06, 0x58, 0xBD, 0x03, 0x80}}, {8, 895, 492, {0x23, 0x7A, 0xE8, 0x0F, 0x5D, 0x0C, 0x8D, 0x06, 0x58, 0x7F, 0x03, 0x80}}, }, /* 20 fps */ { {0, }, {0, }, {0, }, {0, }, }, /* 25 fps */ { {0, }, {0, }, {0, }, {0, }, }, /* 30 fps */ { {0, }, {0, }, {0, }, {0, }, }, }, }; /* * Rom table for kiara chips * * 32 roms tables (one for each resolution ?) * 2 tables per roms (one for each passes) (Y, and U&V) * 128 bytes per passes */ const unsigned int KiaraRomTable [8][2][16][8] = { { /* version 0 */ { /* version 0, passes 0 */ {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000001,0x00000001}, {0x00000000,0x00000000,0x00000009,0x00000009, 0x00000009,0x00000009,0x00000009,0x00000009}, {0x00000000,0x00000000,0x00000009,0x00000049, 0x00000049,0x00000049,0x00000049,0x00000049}, {0x00000000,0x00000000,0x00000049,0x00000049, 0x00000049,0x00000249,0x0000024a,0x00000049}, {0x00000000,0x00000000,0x00000049,0x00000049, 0x00000249,0x00000249,0x0000024a,0x0000024a}, {0x00000000,0x00000000,0x00000049,0x00000249, 0x00000249,0x0000124a,0x0000024a,0x0000024a}, {0x00000000,0x00000000,0x00000049,0x00000249, 0x0000124a,0x00009252,0x00001252,0x00001252}, {0x00000000,0x00000000,0x00000249,0x00000249, 0x00009252,0x00009292,0x00009292,0x00009292}, {0x00000000,0x00000000,0x00000249,0x00001249, 0x00009292,0x00009292,0x00009493,0x000124db}, {0x00000000,0x00000000,0x00000249,0x0000924a, 0x00009492,0x0000a49b,0x0000a49b,0x000124db}, {0x00000000,0x00000000,0x00001249,0x00009252, 0x0000a493,0x000124db,0x000124db,0x000126dc}, {0x00000000,0x00000000,0x00001249,0x00009493, 0x000124db,0x000126dc,0x000136e4,0x000126dc}, {0x00000000,0x00000000,0x00009292,0x0000a49b, 0x000124db,0x000136e4,0x000136e4,0x000136e4}, {0x00000000,0x00000000,0x00009292,0x0000a49b, 0x000126dc,0x0001b724,0x0001b92d,0x0001b925}, {0x00000000,0x00000000,0x00009492,0x000124db, 0x000136e4,0x0001b925,0x0001c96e,0x0001c92d}, {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000} }, { /* version 0, passes 1 */ {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000}, {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000}, {0x00000000,0x00000000,0x00000001,0x00000009, 0x00000009,0x00000009,0x00000009,0x00000001}, {0x00000000,0x00000000,0x00000009,0x00000009, 0x00000049,0x00000049,0x00000049,0x00000049}, {0x00000000,0x00000000,0x00000049,0x00000049, 0x00000049,0x00000049,0x0000024a,0x0000024a}, {0x00000000,0x00000000,0x00000049,0x00000049, 0x00000249,0x00000249,0x0000024a,0x0000024a}, {0x00000000,0x00000000,0x00000049,0x00000249, 0x00000249,0x00000249,0x0000024a,0x00001252}, {0x00000000,0x00000000,0x00000049,0x00001249, 0x0000124a,0x0000124a,0x00001252,0x00009292}, {0x00000000,0x00000000,0x00000249,0x00001249, 0x00009252,0x00009252,0x00009292,0x00009493}, {0x00000000,0x00000000,0x00000249,0x0000924a, 0x00009292,0x00009292,0x00009292,0x00009493}, {0x00000000,0x00000000,0x00000249,0x00009292, 0x00009492,0x00009493,0x0000a49b,0x00009493}, {0x00000000,0x00000000,0x00001249,0x00009292, 0x0000a493,0x000124db,0x000126dc,0x000126dc}, {0x00000000,0x00000000,0x0000924a,0x00009493, 0x0000a493,0x000126dc,0x000136e4,0x000136e4}, {0x00000000,0x00000000,0x00009252,0x00009493, 0x000126dc,0x000126dc,0x000136e4,0x000136e4}, {0x00000000,0x00000000,0x00009292,0x0000a49b, 0x000136e4,0x000136e4,0x0001b725,0x0001b724}, {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000} } }, { /* version 1 */ { /* version 1, passes 0 */ {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000001}, {0x00000000,0x00000000,0x00000009,0x00000009, 0x00000009,0x00000009,0x00000009,0x00000009}, {0x00000000,0x00000000,0x00000049,0x00000049, 0x00000049,0x00000049,0x00000049,0x00000049}, {0x00000000,0x00000000,0x00000049,0x00000049, 0x00000049,0x00000249,0x0000024a,0x0000024a}, {0x00000000,0x00000000,0x00000049,0x00000249, 0x00000249,0x00000249,0x0000024a,0x00001252}, {0x00000000,0x00000000,0x00000249,0x00000249, 0x00000249,0x0000124a,0x00001252,0x00001252}, {0x00000000,0x00000000,0x00000249,0x00000249, 0x0000124a,0x0000124a,0x00009292,0x00009292}, {0x00000000,0x00000000,0x00000249,0x00001249, 0x0000124a,0x00009252,0x00009292,0x00009292}, {0x00000000,0x00000000,0x00000249,0x00001249, 0x00009252,0x00009292,0x00009292,0x00009292}, {0x00000000,0x00000000,0x00000249,0x00001249, 0x00009252,0x00009292,0x00009493,0x00009493}, {0x00000000,0x00000000,0x00000249,0x0000924a, 0x00009252,0x00009493,0x00009493,0x00009493}, {0x00000000,0x00000000,0x00000249,0x0000924a, 0x00009292,0x00009493,0x00009493,0x00009493}, {0x00000000,0x00000000,0x00000249,0x00009252, 0x00009492,0x00009493,0x0000a49b,0x0000a49b}, {0x00000000,0x00000000,0x00001249,0x00009292, 0x00009492,0x000124db,0x000124db,0x000124db}, {0x00000000,0x00000000,0x0000924a,0x00009493, 0x0000a493,0x000126dc,0x000126dc,0x000126dc}, {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000} }, { /* version 1, passes 1 */ {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000}, {0x00000000,0x00000000,0x00000049,0x00000009, 0x00000049,0x00000009,0x00000001,0x00000000}, {0x00000000,0x00000000,0x00000049,0x00000049, 0x00000049,0x00000049,0x00000049,0x00000000}, {0x00000000,0x00000000,0x00000249,0x00000049, 0x00000249,0x00000049,0x0000024a,0x00000001}, {0x00000000,0x00000000,0x00000249,0x00000249, 0x00000249,0x00000249,0x0000024a,0x00000001}, {0x00000000,0x00000000,0x00000249,0x00000249, 0x00000249,0x00000249,0x0000024a,0x00000001}, {0x00000000,0x00000000,0x00000249,0x00000249, 0x00000249,0x00000249,0x0000024a,0x00000009}, {0x00000000,0x00000000,0x00000249,0x00000249, 0x0000124a,0x0000124a,0x0000024a,0x00000009}, {0x00000000,0x00000000,0x00000249,0x00000249, 0x0000124a,0x0000124a,0x0000024a,0x00000009}, {0x00000000,0x00000000,0x00001249,0x00001249, 0x0000124a,0x00009252,0x00001252,0x00000049}, {0x00000000,0x00000000,0x00001249,0x00001249, 0x0000124a,0x00009292,0x00001252,0x00000049}, {0x00000000,0x00000000,0x00001249,0x00001249, 0x0000124a,0x00009292,0x00001252,0x00000049}, {0x00000000,0x00000000,0x00001249,0x00001249, 0x00009252,0x00009292,0x00001252,0x0000024a}, {0x00000000,0x00000000,0x00001249,0x00001249, 0x00009292,0x00009292,0x00001252,0x0000024a}, {0x00000000,0x00000000,0x0000924a,0x0000924a, 0x00009492,0x00009493,0x00009292,0x00001252}, {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000} } }, { /* version 2 */ { /* version 2, passes 0 */ {0x00000000,0x00000000,0x00000049,0x00000049, 0x00000049,0x00000049,0x0000024a,0x0000024a}, {0x00000000,0x00000000,0x00000249,0x00000249, 0x00000249,0x0000124a,0x00001252,0x00009292}, {0x00000000,0x00000000,0x00000249,0x00000249, 0x0000124a,0x00009252,0x00009292,0x00009292}, {0x00000000,0x00000000,0x00000249,0x00001249, 0x0000124a,0x00009292,0x00009493,0x00009493}, {0x00000000,0x00000000,0x00000249,0x00001249, 0x00009252,0x00009493,0x00009493,0x0000a49b}, {0x00000000,0x00000000,0x00000249,0x0000924a, 0x00009292,0x00009493,0x0000a49b,0x0000a49b}, {0x00000000,0x00000000,0x00001249,0x0000924a, 0x00009292,0x00009493,0x0000a49b,0x000124db}, {0x00000000,0x00000000,0x00001249,0x00009252, 0x00009492,0x0000a49b,0x0000a49b,0x000124db}, {0x00000000,0x00000000,0x00001249,0x00009292, 0x00009492,0x000124db,0x000124db,0x000126dc}, {0x00000000,0x00000000,0x00001249,0x00009292, 0x0000a493,0x000124db,0x000126dc,0x000126dc}, {0x00000000,0x00000000,0x00001249,0x00009493, 0x0000a493,0x000124db,0x000126dc,0x000136e4}, {0x00000000,0x00000000,0x00001249,0x00009493, 0x0000a493,0x000126dc,0x000136e4,0x000136e4}, {0x00000000,0x00000000,0x0000924a,0x00009493, 0x0001249b,0x000126dc,0x000136e4,0x000136e4}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x000124db,0x000136e4,0x000136e4,0x0001b724}, {0x00000000,0x00000000,0x00009252,0x000124db, 0x000126dc,0x0001b724,0x0001b725,0x0001b925}, {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000} }, { /* version 2, passes 1 */ {0x00000000,0x00000000,0x00000049,0x00000049, 0x00000049,0x00000049,0x00000049,0x00000049}, {0x00000000,0x00000000,0x00000249,0x00000249, 0x00000249,0x00000249,0x0000024a,0x00000049}, {0x00000000,0x00000000,0x00001249,0x00000249, 0x0000124a,0x0000124a,0x00001252,0x00000049}, {0x00000000,0x00000000,0x00001249,0x00001249, 0x0000124a,0x0000124a,0x00009292,0x0000024a}, {0x00000000,0x00000000,0x00001249,0x00001249, 0x00009252,0x00009292,0x00009292,0x0000024a}, {0x00000000,0x00000000,0x00001249,0x00001249, 0x00009252,0x00009292,0x0000a49b,0x0000024a}, {0x00000000,0x00000000,0x00001249,0x00001249, 0x00009292,0x00009493,0x0000a49b,0x00001252}, {0x00000000,0x00000000,0x00001249,0x00001249, 0x00009292,0x00009493,0x0000a49b,0x00001252}, {0x00000000,0x00000000,0x00001249,0x0000924a, 0x00009492,0x0000a49b,0x0000a49b,0x00001252}, {0x00000000,0x00000000,0x00001249,0x00009252, 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, {0x00000000,0x00000000,0x00001249,0x00009292, 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, {0x00000000,0x00000000,0x00001249,0x00009493, 0x0000a493,0x0000a49b,0x0000a49b,0x00009292}, {0x00000000,0x00000000,0x00001249,0x00009493, 0x0000a493,0x0000a49b,0x0000a49b,0x00009493}, {0x00000000,0x00000000,0x0000924a,0x00009493, 0x0000a493,0x000124db,0x0000a49b,0x00009493}, {0x00000000,0x00000000,0x00009252,0x0000a49b, 0x0001249b,0x000126dc,0x000124db,0x0000a49b}, {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000} } }, { /* version 3 */ { /* version 3, passes 0 */ {0x00000000,0x00000000,0x00000249,0x00000249, 0x0000124a,0x0000124a,0x00009292,0x00009292}, {0x00000000,0x00000000,0x00001249,0x00001249, 0x00009292,0x00009493,0x0000a49b,0x0000a49b}, {0x00000000,0x00000000,0x00001249,0x0000924a, 0x00009492,0x0000a49b,0x0000a49b,0x000124db}, {0x00000000,0x00000000,0x00001249,0x00009292, 0x00009492,0x000124db,0x000126dc,0x000126dc}, {0x00000000,0x00000000,0x00001249,0x00009493, 0x0000a493,0x000124db,0x000126dc,0x000126dc}, {0x00000000,0x00000000,0x00001249,0x00009493, 0x0000a493,0x000126dc,0x000136e4,0x000136e4}, {0x00000000,0x00000000,0x00001249,0x00009493, 0x0000a493,0x000126dc,0x000136e4,0x0001b724}, {0x00000000,0x00000000,0x00001249,0x00009493, 0x0001249b,0x000126dc,0x000136e4,0x0001b724}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0001249b,0x000126dc,0x000136e4,0x0001b724}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0001249b,0x000136e4,0x0001b725,0x0001b724}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x000124db,0x000136e4,0x0001b725,0x0001b925}, {0x00000000,0x00000000,0x00009292,0x0000a49b, 0x000126dc,0x000136e4,0x0001b92d,0x0001b925}, {0x00000000,0x00000000,0x00009292,0x0000a49b, 0x000126dc,0x0001b724,0x0001b92d,0x0001c92d}, {0x00000000,0x00000000,0x00009492,0x000124db, 0x000126dc,0x0001b724,0x0001c96e,0x0001c92d}, {0x00000000,0x00000000,0x0000a492,0x000126db, 0x000136e4,0x0001b925,0x00025bb6,0x00024b77}, {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000} }, { /* version 3, passes 1 */ {0x00000000,0x00000000,0x00001249,0x00000249, 0x0000124a,0x0000124a,0x00001252,0x00001252}, {0x00000000,0x00000000,0x00001249,0x00001249, 0x00009252,0x00009292,0x00009292,0x00001252}, {0x00000000,0x00000000,0x00001249,0x0000924a, 0x00009492,0x00009493,0x0000a49b,0x00001252}, {0x00000000,0x00000000,0x00001249,0x00009252, 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, {0x00000000,0x00000000,0x00001249,0x00009292, 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, {0x00000000,0x00000000,0x00001249,0x00009493, 0x0000a493,0x0000a49b,0x000126dc,0x00009292}, {0x00000000,0x00000000,0x0000924a,0x00009493, 0x0000a493,0x0000a49b,0x000126dc,0x00009493}, {0x00000000,0x00000000,0x0000924a,0x00009493, 0x0000a493,0x0000a49b,0x000126dc,0x00009493}, {0x00000000,0x00000000,0x0000924a,0x00009493, 0x0000a493,0x000124db,0x000126dc,0x00009493}, {0x00000000,0x00000000,0x0000924a,0x00009493, 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0001249b,0x000126dc,0x000126dc,0x0000a49b}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x000124db,0x000136e4,0x000126dc,0x000124db}, {0x00000000,0x00000000,0x00009492,0x0000a49b, 0x000136e4,0x000136e4,0x000126dc,0x000124db}, {0x00000000,0x00000000,0x0000a492,0x000124db, 0x0001b724,0x0001b724,0x000136e4,0x000126dc}, {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000} } }, { /* version 4 */ { /* version 4, passes 0 */ {0x00000000,0x00000000,0x00000049,0x00000049, 0x00000049,0x00000049,0x00000049,0x00000049}, {0x00000000,0x00000000,0x00000249,0x00000049, 0x00000249,0x00000249,0x0000024a,0x00000049}, {0x00000000,0x00000000,0x00000249,0x00000249, 0x0000124a,0x00009252,0x00001252,0x0000024a}, {0x00000000,0x00000000,0x00001249,0x00001249, 0x00009252,0x00009292,0x00009493,0x00001252}, {0x00000000,0x00000000,0x00001249,0x0000924a, 0x00009292,0x00009493,0x00009493,0x00001252}, {0x00000000,0x00000000,0x00001249,0x00009292, 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, {0x00000000,0x00000000,0x00001249,0x00009493, 0x0000a493,0x000124db,0x000124db,0x00009493}, {0x00000000,0x00000000,0x0000924a,0x00009493, 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, {0x00000000,0x00000000,0x0000924a,0x00009493, 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, {0x00000000,0x00000000,0x0000924a,0x00009493, 0x0001249b,0x000126dc,0x000126dc,0x000124db}, {0x00000000,0x00000000,0x00009252,0x00009493, 0x000124db,0x000136e4,0x000136e4,0x000126dc}, {0x00000000,0x00000000,0x00009252,0x0000a49b, 0x000124db,0x000136e4,0x000136e4,0x000126dc}, {0x00000000,0x00000000,0x00009292,0x0000a49b, 0x000126dc,0x000136e4,0x000136e4,0x000136e4}, {0x00000000,0x00000000,0x00009492,0x0000a49b, 0x000126dc,0x0001b724,0x0001b725,0x0001b724}, {0x00000000,0x00000000,0x0000a492,0x000124db, 0x000136e4,0x0001b925,0x0001b92d,0x0001b925}, {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000} }, { /* version 4, passes 1 */ {0x00000000,0x00000000,0x00000249,0x00000049, 0x00000009,0x00000009,0x00000009,0x00000009}, {0x00000000,0x00000000,0x00000249,0x00000249, 0x00000049,0x00000049,0x00000009,0x00000009}, {0x00000000,0x00000000,0x00001249,0x00001249, 0x0000124a,0x00000249,0x00000049,0x00000049}, {0x00000000,0x00000000,0x00001249,0x00001249, 0x0000124a,0x0000124a,0x00000049,0x00000049}, {0x00000000,0x00000000,0x00001249,0x00001249, 0x00009252,0x0000124a,0x0000024a,0x0000024a}, {0x00000000,0x00000000,0x00001249,0x0000924a, 0x00009252,0x0000124a,0x0000024a,0x0000024a}, {0x00000000,0x00000000,0x00001249,0x00009292, 0x00009492,0x00009252,0x00001252,0x00001252}, {0x00000000,0x00000000,0x00001249,0x00009493, 0x0000a493,0x00009292,0x00009292,0x00001252}, {0x00000000,0x00000000,0x0000924a,0x00009493, 0x0000a493,0x00009292,0x00009292,0x00009292}, {0x00000000,0x00000000,0x0000924a,0x00009493, 0x0000a493,0x00009493,0x00009493,0x00009292}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0000a493,0x0000a49b,0x00009493,0x00009493}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0000a493,0x0000a49b,0x0000a49b,0x00009493}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0001249b,0x000124db,0x0000a49b,0x0000a49b}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, {0x00000000,0x00000000,0x00009252,0x000124db, 0x0001b724,0x000136e4,0x000126dc,0x000124db}, {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000} } }, { /* version 5 */ { /* version 5, passes 0 */ {0x00000000,0x00000000,0x00000249,0x00000249, 0x00000249,0x00000249,0x00001252,0x00001252}, {0x00000000,0x00000000,0x00001249,0x00001249, 0x00009252,0x00009292,0x00009292,0x00001252}, {0x00000000,0x00000000,0x00001249,0x0000924a, 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, {0x00000000,0x00000000,0x00001249,0x00009493, 0x0000a493,0x0000a49b,0x000124db,0x00009493}, {0x00000000,0x00000000,0x00001249,0x00009493, 0x0000a493,0x000124db,0x000126dc,0x00009493}, {0x00000000,0x00000000,0x0000924a,0x00009493, 0x0000a493,0x000126dc,0x000126dc,0x0000a49b}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0001249b,0x000126dc,0x000136e4,0x000124db}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x000126dc,0x000136e4,0x000136e4,0x000126dc}, {0x00000000,0x00000000,0x00009292,0x0000a49b, 0x000126dc,0x000136e4,0x000136e4,0x000126dc}, {0x00000000,0x00000000,0x00009292,0x0000a49b, 0x000126dc,0x0001b724,0x0001b725,0x000136e4}, {0x00000000,0x00000000,0x00009292,0x0000a49b, 0x000136e4,0x0001b724,0x0001b92d,0x0001b724}, {0x00000000,0x00000000,0x00009492,0x0000a49b, 0x000136e4,0x0001b724,0x0001b92d,0x0001b724}, {0x00000000,0x00000000,0x00009492,0x000124db, 0x000136e4,0x0001b925,0x0001c96e,0x0001b925}, {0x00000000,0x00000000,0x00009492,0x000124db, 0x0001b724,0x0001b925,0x0001c96e,0x0001c92d}, {0x00000000,0x00000000,0x0000a492,0x000126db, 0x0001c924,0x0002496d,0x00025bb6,0x00024b77}, {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000} }, { /* version 5, passes 1 */ {0x00000000,0x00000000,0x00001249,0x00000249, 0x00000249,0x00000249,0x0000024a,0x0000024a}, {0x00000000,0x00000000,0x00001249,0x00001249, 0x0000124a,0x0000124a,0x0000024a,0x0000024a}, {0x00000000,0x00000000,0x00001249,0x0000924a, 0x00009252,0x00009252,0x0000024a,0x0000024a}, {0x00000000,0x00000000,0x00001249,0x00009292, 0x00009492,0x0000a49b,0x00001252,0x00001252}, {0x00000000,0x00000000,0x0000924a,0x00009493, 0x0000a493,0x0000a49b,0x00001252,0x00001252}, {0x00000000,0x00000000,0x0000924a,0x00009493, 0x0000a493,0x0000a49b,0x00009292,0x00001252}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0000a493,0x0000a49b,0x00009292,0x00009292}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0000a493,0x0000a49b,0x00009493,0x00009292}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0001249b,0x000124db,0x00009493,0x00009292}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0001249b,0x000124db,0x00009493,0x00009493}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x000124db,0x000124db,0x0000a49b,0x00009493}, {0x00000000,0x00000000,0x0000924a,0x000124db, 0x000126dc,0x000126dc,0x0000a49b,0x00009493}, {0x00000000,0x00000000,0x0000924a,0x000124db, 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, {0x00000000,0x00000000,0x00009292,0x000124db, 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, {0x00000000,0x00000000,0x00009492,0x000126db, 0x0001b724,0x000136e4,0x000126dc,0x000124db}, {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000} } }, { /* version 6 */ { /* version 6, passes 0 */ {0x00000000,0x00000000,0x00001249,0x00001249, 0x00009252,0x00009292,0x00009493,0x00009493}, {0x00000000,0x00000000,0x00001249,0x00009292, 0x0000a493,0x0000a49b,0x0000a49b,0x00009493}, {0x00000000,0x00000000,0x00001249,0x00009493, 0x0000a493,0x000124db,0x000124db,0x0000a49b}, {0x00000000,0x00000000,0x0000924a,0x00009493, 0x0000a493,0x000126dc,0x000126dc,0x0000a49b}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0001249b,0x000126dc,0x000136e4,0x000124db}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x000126dc,0x000136e4,0x000136e4,0x000126dc}, {0x00000000,0x00000000,0x00009292,0x0000a49b, 0x000126dc,0x0001b724,0x0001b725,0x000126dc}, {0x00000000,0x00000000,0x00009292,0x0000a49b, 0x000136e4,0x0001b724,0x0001b92d,0x000136e4}, {0x00000000,0x00000000,0x00009492,0x0000a49b, 0x000136e4,0x0001b724,0x0001b92d,0x0001b724}, {0x00000000,0x00000000,0x00009492,0x000124db, 0x000136e4,0x0001b724,0x0001b92d,0x0001b724}, {0x00000000,0x00000000,0x00009492,0x000124db, 0x000136e4,0x0001b925,0x0001b92d,0x0001b925}, {0x00000000,0x00000000,0x00009492,0x000124db, 0x0001b724,0x0001b925,0x0001c96e,0x0001c92d}, {0x00000000,0x00000000,0x0000a492,0x000124db, 0x0001b724,0x0001c92d,0x0001c96e,0x0001c92d}, {0x00000000,0x00000000,0x0000a492,0x000124db, 0x0001b724,0x0001c92d,0x00024b76,0x0002496e}, {0x00000000,0x00000000,0x00012492,0x000126db, 0x0001c924,0x00024b6d,0x0002ddb6,0x00025bbf}, {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000} }, { /* version 6, passes 1 */ {0x00000000,0x00000000,0x00001249,0x00001249, 0x0000124a,0x0000124a,0x00001252,0x00001252}, {0x00000000,0x00000000,0x00001249,0x00009292, 0x00009492,0x00009252,0x00001252,0x00001252}, {0x00000000,0x00000000,0x0000924a,0x00009493, 0x0000a493,0x00009292,0x00001252,0x00001252}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0000a493,0x0000a49b,0x00009292,0x00009292}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0000a493,0x0000a49b,0x00009292,0x00009292}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0001249b,0x0000a49b,0x00009493,0x00009292}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x000124db,0x000124db,0x00009493,0x00009493}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x000124db,0x000124db,0x0000a49b,0x00009493}, {0x00000000,0x00000000,0x0000924a,0x000124db, 0x000126dc,0x000124db,0x0000a49b,0x00009493}, {0x00000000,0x00000000,0x0000924a,0x000124db, 0x000126dc,0x000126dc,0x0000a49b,0x0000a49b}, {0x00000000,0x00000000,0x0000924a,0x000124db, 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, {0x00000000,0x00000000,0x00009492,0x000126db, 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, {0x00000000,0x00000000,0x00009492,0x000126db, 0x0001b724,0x000136e4,0x000126dc,0x000124db}, {0x00000000,0x00000000,0x00009492,0x000126db, 0x0001b724,0x000136e4,0x000126dc,0x000124db}, {0x00000000,0x00000000,0x0000a492,0x000136db, 0x0001c924,0x0001b724,0x000136e4,0x000126dc}, {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000} } }, { /* version 7 */ { /* version 7, passes 0 */ {0x00000000,0x00000000,0x00001249,0x00001249, 0x00009252,0x00009292,0x00009493,0x00009493}, {0x00000000,0x00000000,0x00001249,0x00009493, 0x0000a493,0x000124db,0x000126dc,0x00009493}, {0x00000000,0x00000000,0x00001249,0x0000a49b, 0x0001249b,0x000126dc,0x000126dc,0x0000a49b}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0001249b,0x000126dc,0x000136e4,0x0000a49b}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x000126dc,0x000136e4,0x0001b725,0x000124db}, {0x00000000,0x00000000,0x00009292,0x0000a49b, 0x000136e4,0x0001b724,0x0001b725,0x000126dc}, {0x00000000,0x00000000,0x00009292,0x000124db, 0x000136e4,0x0001b724,0x0001b725,0x000126dc}, {0x00000000,0x00000000,0x00009492,0x000124db, 0x000136e4,0x0001b724,0x0001c96e,0x000136e4}, {0x00000000,0x00000000,0x00009492,0x000124db, 0x000136e4,0x0001c92d,0x0001c96e,0x0001b724}, {0x00000000,0x00000000,0x0000a492,0x000124db, 0x000136e4,0x0001c92d,0x0001c96e,0x0001b724}, {0x00000000,0x00000000,0x0000a492,0x000124db, 0x0001b724,0x0001c92d,0x0001c96e,0x0001b925}, {0x00000000,0x00000000,0x0000a492,0x000126db, 0x0001b724,0x0001c92d,0x00024b76,0x0001c92d}, {0x00000000,0x00000000,0x0000a492,0x000126db, 0x0001b924,0x0001c92d,0x00024b76,0x0001c92d}, {0x00000000,0x00000000,0x0000a492,0x000126db, 0x0001b924,0x0001c92d,0x00024b76,0x0002496e}, {0x00000000,0x00000000,0x00012492,0x000136db, 0x00024924,0x00024b6d,0x0002ddb6,0x00025bbf}, {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000} }, { /* version 7, passes 1 */ {0x00000000,0x00000000,0x00001249,0x00001249, 0x0000124a,0x0000124a,0x00001252,0x00001252}, {0x00000000,0x00000000,0x0000924a,0x00009493, 0x00009492,0x00009292,0x00001252,0x00001252}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0000a493,0x0000a49b,0x00001252,0x00001252}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0000a493,0x0000a49b,0x00009292,0x00009292}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0000a493,0x0000a49b,0x00009292,0x00009292}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x000126dc,0x0000a49b,0x00009493,0x00009292}, {0x00000000,0x00000000,0x0000924a,0x000124db, 0x000126dc,0x000124db,0x00009493,0x00009493}, {0x00000000,0x00000000,0x0000924a,0x000124db, 0x000136e4,0x000124db,0x0000a49b,0x00009493}, {0x00000000,0x00000000,0x0000924a,0x000136db, 0x0001b724,0x000124db,0x0000a49b,0x00009493}, {0x00000000,0x00000000,0x0000924a,0x000136db, 0x0001b724,0x000126dc,0x0000a49b,0x0000a49b}, {0x00000000,0x00000000,0x00009292,0x000136db, 0x0001b724,0x000126dc,0x000124db,0x0000a49b}, {0x00000000,0x00000000,0x00009492,0x000136db, 0x0001b724,0x000126dc,0x000124db,0x0000a49b}, {0x00000000,0x00000000,0x0000a492,0x000136db, 0x0001b724,0x000136e4,0x000126dc,0x000124db}, {0x00000000,0x00000000,0x0000a492,0x000136db, 0x0001b724,0x000136e4,0x000126dc,0x000124db}, {0x00000000,0x00000000,0x00012492,0x0001b6db, 0x0001c924,0x0001b724,0x000136e4,0x000126dc}, {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000} } } }; pwcbsd/pwcbsd/pwc-kiara.h000644 000423 000000 00000002670 10546676137 016133 0ustar00luigiwheel000000 000000 /* * PWCBSD - Philips USB webcam driver for FreeBSD 5.4 and higher * * Copyright (C) 2006 Raaf * * Based on the Linux pwc driver. * * Copyright (C) 1999-2003 Nemosoft Unv. * Copyright (C) 2004-2006 Luc Saillard * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * 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 */ /* Entries for the Kiara (730/740/750) camera */ #ifndef PWC_KIARA_H #define PWC_KIARA_H #include "pwc-ioctl.h" struct Kiara_table_entry { char alternate; /* USB alternate interface */ unsigned short packetsize; /* Normal packet size */ unsigned short bandlength; /* Bandlength when decompressing */ unsigned char mode[12]; /* precomputed mode settings for cam */ }; const extern struct Kiara_table_entry Kiara_table[PSZ_MAX][6][4]; const extern unsigned int KiaraRomTable[8][2][16][8]; #endif pwcbsd/pwcbsd/pwc-nala.h000644 000423 000000 00000004134 10546676137 015754 0ustar00luigiwheel000000 000000 /* * PWCBSD - Philips USB webcam driver for FreeBSD 5.4 and higher * * Copyright (C) 2006 Raaf * * Based on the Linux pwc driver. * * Copyright (C) 1999-2003 Nemosoft Unv. * Copyright (C) 2004-2006 Luc Saillard * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * 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 */ /* SQCIF */ { {0, 0, {0x04, 0x01, 0x03}}, {8, 0, {0x05, 0x01, 0x03}}, {7, 0, {0x08, 0x01, 0x03}}, {7, 0, {0x0A, 0x01, 0x03}}, {6, 0, {0x0C, 0x01, 0x03}}, {5, 0, {0x0F, 0x01, 0x03}}, {4, 0, {0x14, 0x01, 0x03}}, {3, 0, {0x18, 0x01, 0x03}}, }, /* QSIF */ { {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, }, /* QCIF */ { {0, 0, {0x04, 0x01, 0x02}}, {8, 0, {0x05, 0x01, 0x02}}, {7, 0, {0x08, 0x01, 0x02}}, {6, 0, {0x0A, 0x01, 0x02}}, {5, 0, {0x0C, 0x01, 0x02}}, {4, 0, {0x0F, 0x01, 0x02}}, {1, 0, {0x14, 0x01, 0x02}}, {1, 0, {0x18, 0x01, 0x02}}, }, /* SIF */ { {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, }, /* CIF */ { {4, 0, {0x04, 0x01, 0x01}}, {7, 1, {0x05, 0x03, 0x01}}, {6, 1, {0x08, 0x03, 0x01}}, {4, 1, {0x0A, 0x03, 0x01}}, {3, 1, {0x0C, 0x03, 0x01}}, {2, 1, {0x0F, 0x03, 0x01}}, {0}, {0}, }, /* VGA */ { {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, }, pwcbsd/pwcbsd/pwc-timon.c000644 000423 000000 00000205337 10546676137 016172 0ustar00luigiwheel000000 000000 /* * PWCBSD - Philips USB webcam driver for FreeBSD 5.4 and higher * * Copyright (C) 2006 Raaf * * Based on the Linux pwc driver. * * Copyright (C) 1999-2003 Nemosoft Unv. * Copyright (C) 2004-2006 Luc Saillard * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * 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 */ /* This tables contains entries for the 675/680/690 (Timon) camera, with 4 different qualities (no compression, low, medium, high). It lists the bandwidth requirements for said mode by its alternate interface number. An alternate of 0 means that the mode is unavailable. There are 6 * 4 * 4 entries: 6 different resolutions subqcif, qsif, qcif, sif, cif, vga 6 framerates: 5, 10, 15, 20, 25, 30 4 compression modi: none, low, medium, high When an uncompressed mode is not available, the next available compressed mode will be chosen (unless the decompressor is absent). Sometimes there are only 1 or 2 compressed modes available; in that case entries are duplicated. */ #include "pwc-timon.h" const struct Timon_table_entry Timon_table[PSZ_MAX][6][4] = { /* SQCIF */ { /* 5 fps */ { {1, 140, 0, {0x05, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x8C, 0xFC, 0x80, 0x02}}, {1, 140, 0, {0x05, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x8C, 0xFC, 0x80, 0x02}}, {1, 140, 0, {0x05, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x8C, 0xFC, 0x80, 0x02}}, {1, 140, 0, {0x05, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x8C, 0xFC, 0x80, 0x02}}, }, /* 10 fps */ { {2, 280, 0, {0x04, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x18, 0xA9, 0x80, 0x02}}, {2, 280, 0, {0x04, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x18, 0xA9, 0x80, 0x02}}, {2, 280, 0, {0x04, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x18, 0xA9, 0x80, 0x02}}, {2, 280, 0, {0x04, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x18, 0xA9, 0x80, 0x02}}, }, /* 15 fps */ { {3, 410, 0, {0x03, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x9A, 0x71, 0x80, 0x02}}, {3, 410, 0, {0x03, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x9A, 0x71, 0x80, 0x02}}, {3, 410, 0, {0x03, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x9A, 0x71, 0x80, 0x02}}, {3, 410, 0, {0x03, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x9A, 0x71, 0x80, 0x02}}, }, /* 20 fps */ { {4, 559, 0, {0x02, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x2F, 0x56, 0x80, 0x02}}, {4, 559, 0, {0x02, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x2F, 0x56, 0x80, 0x02}}, {4, 559, 0, {0x02, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x2F, 0x56, 0x80, 0x02}}, {4, 559, 0, {0x02, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x2F, 0x56, 0x80, 0x02}}, }, /* 25 fps */ { {5, 659, 0, {0x01, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x93, 0x46, 0x80, 0x02}}, {5, 659, 0, {0x01, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x93, 0x46, 0x80, 0x02}}, {5, 659, 0, {0x01, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x93, 0x46, 0x80, 0x02}}, {5, 659, 0, {0x01, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x93, 0x46, 0x80, 0x02}}, }, /* 30 fps */ { {7, 838, 0, {0x00, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x46, 0x3B, 0x80, 0x02}}, {7, 838, 0, {0x00, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x46, 0x3B, 0x80, 0x02}}, {7, 838, 0, {0x00, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x46, 0x3B, 0x80, 0x02}}, {7, 838, 0, {0x00, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x46, 0x3B, 0x80, 0x02}}, }, }, /* QSIF */ { /* 5 fps */ { {1, 146, 0, {0x2D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0xFC, 0xC0, 0x02}}, {1, 146, 0, {0x2D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0xFC, 0xC0, 0x02}}, {1, 146, 0, {0x2D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0xFC, 0xC0, 0x02}}, {1, 146, 0, {0x2D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0xFC, 0xC0, 0x02}}, }, /* 10 fps */ { {2, 291, 0, {0x2C, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x23, 0xA1, 0xC0, 0x02}}, {1, 191, 630, {0x2C, 0xF4, 0x05, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xBF, 0xF4, 0xC0, 0x02}}, {1, 191, 630, {0x2C, 0xF4, 0x05, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xBF, 0xF4, 0xC0, 0x02}}, {1, 191, 630, {0x2C, 0xF4, 0x05, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xBF, 0xF4, 0xC0, 0x02}}, }, /* 15 fps */ { {3, 437, 0, {0x2B, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0xB5, 0x6D, 0xC0, 0x02}}, {2, 291, 640, {0x2B, 0xF4, 0x05, 0x13, 0xF7, 0x13, 0x2F, 0x13, 0x08, 0x23, 0xA1, 0xC0, 0x02}}, {2, 291, 640, {0x2B, 0xF4, 0x05, 0x13, 0xF7, 0x13, 0x2F, 0x13, 0x08, 0x23, 0xA1, 0xC0, 0x02}}, {1, 191, 420, {0x2B, 0xF4, 0x0D, 0x0D, 0x1B, 0x0C, 0x53, 0x1E, 0x08, 0xBF, 0xF4, 0xC0, 0x02}}, }, /* 20 fps */ { {4, 588, 0, {0x2A, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x4C, 0x52, 0xC0, 0x02}}, {3, 447, 730, {0x2A, 0xF4, 0x05, 0x16, 0xC9, 0x16, 0x01, 0x0E, 0x18, 0xBF, 0x69, 0xC0, 0x02}}, {2, 292, 476, {0x2A, 0xF4, 0x0D, 0x0E, 0xD8, 0x0E, 0x10, 0x19, 0x18, 0x24, 0xA1, 0xC0, 0x02}}, {1, 192, 312, {0x2A, 0xF4, 0x1D, 0x09, 0xB3, 0x08, 0xEB, 0x1E, 0x18, 0xC0, 0xF4, 0xC0, 0x02}}, }, /* 25 fps */ { {5, 703, 0, {0x29, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0xBF, 0x42, 0xC0, 0x02}}, {3, 447, 610, {0x29, 0xF4, 0x05, 0x13, 0x0B, 0x12, 0x43, 0x14, 0x18, 0xBF, 0x69, 0xC0, 0x02}}, {2, 292, 398, {0x29, 0xF4, 0x0D, 0x0C, 0x6C, 0x0B, 0xA4, 0x1E, 0x18, 0x24, 0xA1, 0xC0, 0x02}}, {1, 192, 262, {0x29, 0xF4, 0x25, 0x08, 0x23, 0x07, 0x5B, 0x1E, 0x18, 0xC0, 0xF4, 0xC0, 0x02}}, }, /* 30 fps */ { {8, 873, 0, {0x28, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x69, 0x37, 0xC0, 0x02}}, {5, 704, 774, {0x28, 0xF4, 0x05, 0x18, 0x21, 0x17, 0x59, 0x0F, 0x18, 0xC0, 0x42, 0xC0, 0x02}}, {3, 448, 492, {0x28, 0xF4, 0x05, 0x0F, 0x5D, 0x0E, 0x95, 0x15, 0x18, 0xC0, 0x69, 0xC0, 0x02}}, {2, 291, 320, {0x28, 0xF4, 0x1D, 0x09, 0xFB, 0x09, 0x33, 0x1E, 0x18, 0x23, 0xA1, 0xC0, 0x02}}, }, }, /* QCIF */ { /* 5 fps */ { {1, 193, 0, {0x0D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xC1, 0xF4, 0xC0, 0x02}}, {1, 193, 0, {0x0D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xC1, 0xF4, 0xC0, 0x02}}, {1, 193, 0, {0x0D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xC1, 0xF4, 0xC0, 0x02}}, {1, 193, 0, {0x0D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xC1, 0xF4, 0xC0, 0x02}}, }, /* 10 fps */ { {3, 385, 0, {0x0C, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x81, 0x79, 0xC0, 0x02}}, {2, 291, 800, {0x0C, 0xF4, 0x05, 0x18, 0xF4, 0x18, 0x18, 0x11, 0x08, 0x23, 0xA1, 0xC0, 0x02}}, {2, 291, 800, {0x0C, 0xF4, 0x05, 0x18, 0xF4, 0x18, 0x18, 0x11, 0x08, 0x23, 0xA1, 0xC0, 0x02}}, {1, 194, 532, {0x0C, 0xF4, 0x05, 0x10, 0x9A, 0x0F, 0xBE, 0x1B, 0x08, 0xC2, 0xF0, 0xC0, 0x02}}, }, /* 15 fps */ { {4, 577, 0, {0x0B, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x41, 0x52, 0xC0, 0x02}}, {3, 447, 818, {0x0B, 0xF4, 0x05, 0x19, 0x89, 0x18, 0xAD, 0x0F, 0x10, 0xBF, 0x69, 0xC0, 0x02}}, {2, 292, 534, {0x0B, 0xF4, 0x05, 0x10, 0xA3, 0x0F, 0xC7, 0x19, 0x10, 0x24, 0xA1, 0xC0, 0x02}}, {1, 195, 356, {0x0B, 0xF4, 0x15, 0x0B, 0x11, 0x0A, 0x35, 0x1E, 0x10, 0xC3, 0xF0, 0xC0, 0x02}}, }, /* 20 fps */ { {6, 776, 0, {0x0A, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x08, 0x3F, 0xC0, 0x02}}, {4, 591, 804, {0x0A, 0xF4, 0x05, 0x19, 0x1E, 0x18, 0x42, 0x0F, 0x18, 0x4F, 0x4E, 0xC0, 0x02}}, {3, 447, 608, {0x0A, 0xF4, 0x05, 0x12, 0xFD, 0x12, 0x21, 0x15, 0x18, 0xBF, 0x69, 0xC0, 0x02}}, {2, 291, 396, {0x0A, 0xF4, 0x15, 0x0C, 0x5E, 0x0B, 0x82, 0x1E, 0x18, 0x23, 0xA1, 0xC0, 0x02}}, }, /* 25 fps */ { {9, 928, 0, {0x09, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xA0, 0x33, 0xC0, 0x02}}, {5, 703, 800, {0x09, 0xF4, 0x05, 0x18, 0xF4, 0x18, 0x18, 0x10, 0x18, 0xBF, 0x42, 0xC0, 0x02}}, {3, 447, 508, {0x09, 0xF4, 0x0D, 0x0F, 0xD2, 0x0E, 0xF6, 0x1B, 0x18, 0xBF, 0x69, 0xC0, 0x02}}, {2, 292, 332, {0x09, 0xF4, 0x1D, 0x0A, 0x5A, 0x09, 0x7E, 0x1E, 0x18, 0x24, 0xA1, 0xC0, 0x02}}, }, /* 30 fps */ { {0, }, {9, 956, 876, {0x08, 0xF4, 0x05, 0x1B, 0x58, 0x1A, 0x7C, 0x0E, 0x20, 0xBC, 0x33, 0x10, 0x02}}, {4, 592, 542, {0x08, 0xF4, 0x05, 0x10, 0xE4, 0x10, 0x08, 0x17, 0x20, 0x50, 0x4E, 0x10, 0x02}}, {2, 291, 266, {0x08, 0xF4, 0x25, 0x08, 0x48, 0x07, 0x6C, 0x1E, 0x20, 0x23, 0xA1, 0x10, 0x02}}, }, }, /* SIF */ { /* 5 fps */ { {4, 582, 0, {0x35, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x46, 0x52, 0x60, 0x02}}, {3, 387, 1276, {0x35, 0xF4, 0x05, 0x27, 0xD8, 0x26, 0x48, 0x03, 0x10, 0x83, 0x79, 0x60, 0x02}}, {2, 291, 960, {0x35, 0xF4, 0x0D, 0x1D, 0xF2, 0x1C, 0x62, 0x04, 0x10, 0x23, 0xA1, 0x60, 0x02}}, {1, 191, 630, {0x35, 0xF4, 0x1D, 0x13, 0xA9, 0x12, 0x19, 0x05, 0x08, 0xBF, 0xF4, 0x60, 0x02}}, }, /* 10 fps */ { {0, }, {6, 775, 1278, {0x34, 0xF4, 0x05, 0x27, 0xE8, 0x26, 0x58, 0x05, 0x30, 0x07, 0x3F, 0x10, 0x02}}, {3, 447, 736, {0x34, 0xF4, 0x15, 0x16, 0xFB, 0x15, 0x6B, 0x05, 0x18, 0xBF, 0x69, 0x10, 0x02}}, {2, 291, 480, {0x34, 0xF4, 0x2D, 0x0E, 0xF9, 0x0D, 0x69, 0x09, 0x18, 0x23, 0xA1, 0x10, 0x02}}, }, /* 15 fps */ { {0, }, {9, 955, 1050, {0x33, 0xF4, 0x05, 0x20, 0xCF, 0x1F, 0x3F, 0x06, 0x48, 0xBB, 0x33, 0x10, 0x02}}, {4, 591, 650, {0x33, 0xF4, 0x15, 0x14, 0x44, 0x12, 0xB4, 0x08, 0x30, 0x4F, 0x4E, 0x10, 0x02}}, {3, 448, 492, {0x33, 0xF4, 0x25, 0x0F, 0x52, 0x0D, 0xC2, 0x09, 0x28, 0xC0, 0x69, 0x10, 0x02}}, }, /* 20 fps */ { {0, }, {9, 958, 782, {0x32, 0xF4, 0x0D, 0x18, 0x6A, 0x16, 0xDA, 0x0B, 0x58, 0xBE, 0x33, 0xD0, 0x02}}, {5, 703, 574, {0x32, 0xF4, 0x1D, 0x11, 0xE7, 0x10, 0x57, 0x0B, 0x40, 0xBF, 0x42, 0xD0, 0x02}}, {3, 446, 364, {0x32, 0xF4, 0x3D, 0x0B, 0x5C, 0x09, 0xCC, 0x0E, 0x30, 0xBE, 0x69, 0xD0, 0x02}}, }, /* 25 fps */ { {0, }, {9, 958, 654, {0x31, 0xF4, 0x15, 0x14, 0x66, 0x12, 0xD6, 0x0B, 0x50, 0xBE, 0x33, 0x90, 0x02}}, {6, 776, 530, {0x31, 0xF4, 0x25, 0x10, 0x8C, 0x0E, 0xFC, 0x0C, 0x48, 0x08, 0x3F, 0x90, 0x02}}, {4, 592, 404, {0x31, 0xF4, 0x35, 0x0C, 0x96, 0x0B, 0x06, 0x0B, 0x38, 0x50, 0x4E, 0x90, 0x02}}, }, /* 30 fps */ { {0, }, {9, 957, 526, {0x30, 0xF4, 0x25, 0x10, 0x68, 0x0E, 0xD8, 0x0D, 0x58, 0xBD, 0x33, 0x60, 0x02}}, {6, 775, 426, {0x30, 0xF4, 0x35, 0x0D, 0x48, 0x0B, 0xB8, 0x0F, 0x50, 0x07, 0x3F, 0x60, 0x02}}, {4, 590, 324, {0x30, 0x7A, 0x4B, 0x0A, 0x1C, 0x08, 0xB4, 0x0E, 0x40, 0x4E, 0x52, 0x60, 0x02}}, }, }, /* CIF */ { /* 5 fps */ { {6, 771, 0, {0x15, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x3F, 0x80, 0x02}}, {4, 465, 1278, {0x15, 0xF4, 0x05, 0x27, 0xEE, 0x26, 0x36, 0x03, 0x18, 0xD1, 0x65, 0x80, 0x02}}, {2, 291, 800, {0x15, 0xF4, 0x15, 0x18, 0xF4, 0x17, 0x3C, 0x05, 0x18, 0x23, 0xA1, 0x80, 0x02}}, {1, 193, 528, {0x15, 0xF4, 0x2D, 0x10, 0x7E, 0x0E, 0xC6, 0x0A, 0x18, 0xC1, 0xF4, 0x80, 0x02}}, }, /* 10 fps */ { {0, }, {9, 932, 1278, {0x14, 0xF4, 0x05, 0x27, 0xEE, 0x26, 0x36, 0x04, 0x30, 0xA4, 0x33, 0x10, 0x02}}, {4, 591, 812, {0x14, 0xF4, 0x15, 0x19, 0x56, 0x17, 0x9E, 0x06, 0x28, 0x4F, 0x4E, 0x10, 0x02}}, {2, 291, 400, {0x14, 0xF4, 0x3D, 0x0C, 0x7A, 0x0A, 0xC2, 0x0E, 0x28, 0x23, 0xA1, 0x10, 0x02}}, }, /* 15 fps */ { {0, }, {9, 956, 876, {0x13, 0xF4, 0x0D, 0x1B, 0x58, 0x19, 0xA0, 0x05, 0x38, 0xBC, 0x33, 0x60, 0x02}}, {5, 703, 644, {0x13, 0xF4, 0x1D, 0x14, 0x1C, 0x12, 0x64, 0x08, 0x38, 0xBF, 0x42, 0x60, 0x02}}, {3, 448, 410, {0x13, 0xF4, 0x3D, 0x0C, 0xC4, 0x0B, 0x0C, 0x0E, 0x38, 0xC0, 0x69, 0x60, 0x02}}, }, /* 20 fps */ { {0, }, {9, 956, 650, {0x12, 0xF4, 0x1D, 0x14, 0x4A, 0x12, 0x92, 0x09, 0x48, 0xBC, 0x33, 0x10, 0x03}}, {6, 776, 528, {0x12, 0xF4, 0x2D, 0x10, 0x7E, 0x0E, 0xC6, 0x0A, 0x40, 0x08, 0x3F, 0x10, 0x03}}, {4, 591, 402, {0x12, 0xF4, 0x3D, 0x0C, 0x8F, 0x0A, 0xD7, 0x0E, 0x40, 0x4F, 0x4E, 0x10, 0x03}}, }, /* 25 fps */ { {0, }, {9, 956, 544, {0x11, 0xF4, 0x25, 0x10, 0xF4, 0x0F, 0x3C, 0x0A, 0x48, 0xBC, 0x33, 0xC0, 0x02}}, {7, 840, 478, {0x11, 0xF4, 0x2D, 0x0E, 0xEB, 0x0D, 0x33, 0x0B, 0x48, 0x48, 0x3B, 0xC0, 0x02}}, {5, 703, 400, {0x11, 0xF4, 0x3D, 0x0C, 0x7A, 0x0A, 0xC2, 0x0E, 0x48, 0xBF, 0x42, 0xC0, 0x02}}, }, /* 30 fps */ { {0, }, {9, 956, 438, {0x10, 0xF4, 0x35, 0x0D, 0xAC, 0x0B, 0xF4, 0x0D, 0x50, 0xBC, 0x33, 0x10, 0x02}}, {7, 838, 384, {0x10, 0xF4, 0x45, 0x0B, 0xFD, 0x0A, 0x45, 0x0F, 0x50, 0x46, 0x3B, 0x10, 0x02}}, {6, 773, 354, {0x10, 0x7A, 0x4B, 0x0B, 0x0C, 0x09, 0x80, 0x10, 0x50, 0x05, 0x3F, 0x10, 0x02}}, }, }, /* VGA */ { /* 5 fps */ { {0, }, {6, 773, 1272, {0x1D, 0xF4, 0x15, 0x27, 0xB6, 0x24, 0x96, 0x02, 0x30, 0x05, 0x3F, 0x10, 0x02}}, {4, 592, 976, {0x1D, 0xF4, 0x25, 0x1E, 0x78, 0x1B, 0x58, 0x03, 0x30, 0x50, 0x4E, 0x10, 0x02}}, {3, 448, 738, {0x1D, 0xF4, 0x3D, 0x17, 0x0C, 0x13, 0xEC, 0x04, 0x30, 0xC0, 0x69, 0x10, 0x02}}, }, /* 10 fps */ { {0, }, {9, 956, 788, {0x1C, 0xF4, 0x35, 0x18, 0x9C, 0x15, 0x7C, 0x03, 0x48, 0xBC, 0x33, 0x10, 0x02}}, {6, 776, 640, {0x1C, 0x7A, 0x53, 0x13, 0xFC, 0x11, 0x2C, 0x04, 0x48, 0x08, 0x3F, 0x10, 0x02}}, {4, 592, 488, {0x1C, 0x7A, 0x6B, 0x0F, 0x3C, 0x0C, 0x6C, 0x06, 0x48, 0x50, 0x4E, 0x10, 0x02}}, }, /* 15 fps */ { {0, }, {9, 957, 526, {0x1B, 0x7A, 0x63, 0x10, 0x68, 0x0D, 0x98, 0x06, 0x58, 0xBD, 0x33, 0x80, 0x02}}, {9, 957, 526, {0x1B, 0x7A, 0x63, 0x10, 0x68, 0x0D, 0x98, 0x06, 0x58, 0xBD, 0x33, 0x80, 0x02}}, {8, 895, 492, {0x1B, 0x7A, 0x6B, 0x0F, 0x5D, 0x0C, 0x8D, 0x06, 0x58, 0x7F, 0x37, 0x80, 0x02}}, }, /* 20 fps */ { {0, }, {0, }, {0, }, {0, }, }, /* 25 fps */ { {0, }, {0, }, {0, }, {0, }, }, /* 30 fps */ { {0, }, {0, }, {0, }, {0, }, }, }, }; /* * 16 versions: * 2 tables (one for Y, and one for U&V) * 16 levels of details per tables * 8 blocs */ const unsigned int TimonRomTable [16][2][16][8] = { { /* version 0 */ { /* version 0, passes 0 */ {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000001}, {0x00000000,0x00000000,0x00000001,0x00000001, 0x00000001,0x00000001,0x00000001,0x00000001}, {0x00000000,0x00000000,0x00000001,0x00000001, 0x00000001,0x00000009,0x00000009,0x00000009}, {0x00000000,0x00000000,0x00000009,0x00000001, 0x00000009,0x00000009,0x00000009,0x00000009}, {0x00000000,0x00000000,0x00000009,0x00000009, 0x00000009,0x00000009,0x00000049,0x00000009}, {0x00000000,0x00000000,0x00000009,0x00000009, 0x00000009,0x00000049,0x00000049,0x00000049}, {0x00000000,0x00000000,0x00000009,0x00000009, 0x00000049,0x00000049,0x00000049,0x00000049}, {0x00000000,0x00000000,0x00000009,0x00000049, 0x00000049,0x00000049,0x00000049,0x00000049}, {0x00000000,0x00000000,0x00000049,0x00000049, 0x00000049,0x00000049,0x0000024a,0x0000024a}, {0x00000000,0x00000000,0x00000049,0x00000049, 0x00000049,0x00000249,0x0000024a,0x0000024a}, {0x00000000,0x00000000,0x00000049,0x00000049, 0x00000249,0x00000249,0x0000024a,0x0000024a}, {0x00000000,0x00000000,0x00000049,0x00000049, 0x00000249,0x00000249,0x00001252,0x0000024a}, {0x00000000,0x00000000,0x00000049,0x00000049, 0x00000249,0x0000124a,0x00001252,0x0000024a}, {0x00000000,0x00000000,0x00000049,0x00000249, 0x00000249,0x0000124a,0x00001252,0x0000024a}, {0x00000000,0x00000000,0x00000249,0x00001249, 0x0000124a,0x00009252,0x00009292,0x00001252}, {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000} }, { /* version 0, passes 1 */ {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000}, {0x00000000,0x00000000,0x00000001,0x00000001, 0x00000001,0x00000001,0x00000000,0x00000000}, {0x00000000,0x00000000,0x00000009,0x00000001, 0x00000001,0x00000009,0x00000000,0x00000000}, {0x00000000,0x00000000,0x00000009,0x00000009, 0x00000009,0x00000009,0x00000000,0x00000000}, {0x00000000,0x00000000,0x00000009,0x00000009, 0x00000009,0x00000009,0x00000001,0x00000000}, {0x00000000,0x00000000,0x00000049,0x00000009, 0x00000009,0x00000049,0x00000001,0x00000001}, {0x00000000,0x00000000,0x00000049,0x00000009, 0x00000009,0x00000049,0x00000001,0x00000001}, {0x00000000,0x00000000,0x00000049,0x00000049, 0x00000049,0x00000049,0x00000009,0x00000001}, {0x00000000,0x00000000,0x00000049,0x00000049, 0x00000049,0x00000049,0x00000009,0x00000001}, {0x00000000,0x00000000,0x00000049,0x00000049, 0x00000049,0x00000049,0x00000009,0x00000001}, {0x00000000,0x00000000,0x00000049,0x00000049, 0x00000049,0x00000049,0x00000009,0x00000009}, {0x00000000,0x00000000,0x00000049,0x00000049, 0x00000049,0x00000249,0x00000049,0x00000009}, {0x00000000,0x00000000,0x00000049,0x00000049, 0x00000049,0x00000249,0x00000049,0x00000009}, {0x00000000,0x00000000,0x00000249,0x00000049, 0x00000249,0x00000249,0x00000049,0x00000009}, {0x00000000,0x00000000,0x00001249,0x00000249, 0x0000124a,0x0000124a,0x0000024a,0x00000049}, {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000} } }, { /* version 1 */ { /* version 1, passes 0 */ {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000001}, {0x00000000,0x00000000,0x00000001,0x00000001, 0x00000001,0x00000009,0x00000009,0x00000009}, {0x00000000,0x00000000,0x00000009,0x00000009, 0x00000009,0x00000009,0x00000009,0x00000009}, {0x00000000,0x00000000,0x00000009,0x00000009, 0x00000009,0x00000049,0x00000049,0x00000049}, {0x00000000,0x00000000,0x00000009,0x00000049, 0x00000049,0x00000049,0x00000049,0x00000049}, {0x00000000,0x00000000,0x00000049,0x00000049, 0x00000049,0x00000249,0x0000024a,0x0000024a}, {0x00000000,0x00000000,0x00000049,0x00000049, 0x00000249,0x00000249,0x0000024a,0x0000024a}, {0x00000000,0x00000000,0x00000049,0x00000249, 0x00000249,0x00000249,0x0000024a,0x00001252}, {0x00000000,0x00000000,0x00000049,0x00000249, 0x00000249,0x0000124a,0x00001252,0x00001252}, {0x00000000,0x00000000,0x00000049,0x00000249, 0x0000124a,0x0000124a,0x00001252,0x00001252}, {0x00000000,0x00000000,0x00000249,0x00000249, 0x0000124a,0x0000124a,0x00009292,0x00009292}, {0x00000000,0x00000000,0x00000249,0x00001249, 0x0000124a,0x00009252,0x00009292,0x00009292}, {0x00000000,0x00000000,0x00000249,0x00001249, 0x00009252,0x00009252,0x00009292,0x00009292}, {0x00000000,0x00000000,0x00000249,0x0000924a, 0x00009292,0x00009493,0x00009493,0x00009493}, {0x00000000,0x00000000,0x00001249,0x00009252, 0x00009492,0x0000a49b,0x0000a49b,0x0000a49b}, {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000} }, { /* version 1, passes 1 */ {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000}, {0x00000000,0x00000000,0x00000009,0x00000009, 0x00000009,0x00000001,0x00000001,0x00000000}, {0x00000000,0x00000000,0x00000009,0x00000009, 0x00000009,0x00000009,0x00000001,0x00000000}, {0x00000000,0x00000000,0x00000049,0x00000049, 0x00000049,0x00000009,0x00000001,0x00000000}, {0x00000000,0x00000000,0x00000049,0x00000049, 0x00000049,0x00000049,0x00000001,0x00000001}, {0x00000000,0x00000000,0x00000049,0x00000049, 0x00000049,0x00000049,0x00000009,0x00000001}, {0x00000000,0x00000000,0x00000249,0x00000049, 0x00000049,0x00000249,0x00000009,0x00000001}, {0x00000000,0x00000000,0x00000249,0x00000049, 0x00000249,0x00000249,0x00000009,0x00000009}, {0x00000000,0x00000000,0x00000249,0x00000249, 0x00000249,0x00000249,0x00000049,0x00000009}, {0x00000000,0x00000000,0x00000249,0x00000249, 0x00000249,0x0000124a,0x00000049,0x00000009}, {0x00000000,0x00000000,0x00000249,0x00000249, 0x00000249,0x0000124a,0x00000049,0x00000009}, {0x00000000,0x00000000,0x00000249,0x00000249, 0x00000249,0x0000124a,0x0000024a,0x00000049}, {0x00000000,0x00000000,0x00000249,0x00000249, 0x0000124a,0x0000124a,0x0000024a,0x00000049}, {0x00000000,0x00000000,0x00000249,0x00000249, 0x0000124a,0x0000124a,0x0000024a,0x00000049}, {0x00000000,0x00000000,0x00001249,0x00001249, 0x00009252,0x00009252,0x00001252,0x0000024a}, {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000} } }, { /* version 2 */ { /* version 2, passes 0 */ {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000001}, {0x00000000,0x00000000,0x00000009,0x00000009, 0x00000009,0x00000009,0x00000009,0x00000009}, {0x00000000,0x00000000,0x00000049,0x00000049, 0x00000049,0x00000049,0x00000049,0x00000049}, {0x00000000,0x00000000,0x00000049,0x00000049, 0x00000049,0x00000249,0x0000024a,0x0000024a}, {0x00000000,0x00000000,0x00000049,0x00000249, 0x00000249,0x00000249,0x0000024a,0x00001252}, {0x00000000,0x00000000,0x00000249,0x00000249, 0x00000249,0x0000124a,0x00001252,0x00001252}, {0x00000000,0x00000000,0x00000249,0x00000249, 0x0000124a,0x0000124a,0x00009292,0x00009292}, {0x00000000,0x00000000,0x00000249,0x00001249, 0x0000124a,0x00009252,0x00009292,0x00009292}, {0x00000000,0x00000000,0x00000249,0x00001249, 0x00009252,0x00009292,0x00009292,0x00009292}, {0x00000000,0x00000000,0x00000249,0x00001249, 0x00009252,0x00009292,0x00009493,0x00009493}, {0x00000000,0x00000000,0x00000249,0x0000924a, 0x00009252,0x00009493,0x00009493,0x00009493}, {0x00000000,0x00000000,0x00000249,0x0000924a, 0x00009292,0x00009493,0x00009493,0x00009493}, {0x00000000,0x00000000,0x00000249,0x00009252, 0x00009492,0x00009493,0x0000a49b,0x0000a49b}, {0x00000000,0x00000000,0x00001249,0x00009292, 0x00009492,0x000124db,0x000124db,0x000124db}, {0x00000000,0x00000000,0x0000924a,0x00009493, 0x0000a493,0x000126dc,0x000126dc,0x000126dc}, {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000} }, { /* version 2, passes 1 */ {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000}, {0x00000000,0x00000000,0x00000049,0x00000009, 0x00000049,0x00000009,0x00000001,0x00000000}, {0x00000000,0x00000000,0x00000049,0x00000049, 0x00000049,0x00000049,0x00000049,0x00000000}, {0x00000000,0x00000000,0x00000249,0x00000049, 0x00000249,0x00000049,0x0000024a,0x00000001}, {0x00000000,0x00000000,0x00000249,0x00000249, 0x00000249,0x00000249,0x0000024a,0x00000001}, {0x00000000,0x00000000,0x00000249,0x00000249, 0x00000249,0x00000249,0x0000024a,0x00000001}, {0x00000000,0x00000000,0x00000249,0x00000249, 0x00000249,0x00000249,0x0000024a,0x00000009}, {0x00000000,0x00000000,0x00000249,0x00000249, 0x0000124a,0x0000124a,0x0000024a,0x00000009}, {0x00000000,0x00000000,0x00000249,0x00000249, 0x0000124a,0x0000124a,0x0000024a,0x00000009}, {0x00000000,0x00000000,0x00001249,0x00001249, 0x0000124a,0x00009252,0x00001252,0x00000049}, {0x00000000,0x00000000,0x00001249,0x00001249, 0x0000124a,0x00009292,0x00001252,0x00000049}, {0x00000000,0x00000000,0x00001249,0x00001249, 0x0000124a,0x00009292,0x00001252,0x00000049}, {0x00000000,0x00000000,0x00001249,0x00001249, 0x00009252,0x00009292,0x00001252,0x0000024a}, {0x00000000,0x00000000,0x00001249,0x00001249, 0x00009292,0x00009292,0x00001252,0x0000024a}, {0x00000000,0x00000000,0x0000924a,0x0000924a, 0x00009492,0x00009493,0x00009292,0x00001252}, {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000} } }, { /* version 3 */ { /* version 3, passes 0 */ {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000001}, {0x00000000,0x00000000,0x00000049,0x00000049, 0x00000049,0x00000049,0x00000049,0x00000049}, {0x00000000,0x00000000,0x00000049,0x00000249, 0x00000249,0x00000249,0x00001252,0x0000024a}, {0x00000000,0x00000000,0x00000249,0x00000249, 0x00000249,0x0000124a,0x00001252,0x00001252}, {0x00000000,0x00000000,0x00000249,0x00000249, 0x0000124a,0x00009252,0x00009292,0x00009292}, {0x00000000,0x00000000,0x00000249,0x00001249, 0x0000124a,0x00009292,0x00009292,0x00009493}, {0x00000000,0x00000000,0x00000249,0x00001249, 0x00009252,0x00009292,0x00009493,0x00009493}, {0x00000000,0x00000000,0x00000249,0x00001249, 0x00009292,0x00009493,0x00009493,0x00009493}, {0x00000000,0x00000000,0x00000249,0x00009252, 0x00009292,0x00009493,0x0000a49b,0x0000a49b}, {0x00000000,0x00000000,0x00001249,0x00009252, 0x00009292,0x0000a49b,0x0000a49b,0x0000a49b}, {0x00000000,0x00000000,0x00001249,0x00009252, 0x00009492,0x0000a49b,0x0000a49b,0x0000a49b}, {0x00000000,0x00000000,0x00001249,0x00009292, 0x00009492,0x0000a49b,0x000124db,0x000124db}, {0x00000000,0x00000000,0x00001249,0x00009292, 0x0000a493,0x0000a49b,0x000124db,0x000124db}, {0x00000000,0x00000000,0x00001249,0x00009493, 0x0001249b,0x000126dc,0x000136e4,0x000126dc}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x000124db,0x000136e4,0x0001b725,0x000136e4}, {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000} }, { /* version 3, passes 1 */ {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000}, {0x00000000,0x00000000,0x00000049,0x00000049, 0x00000049,0x00000049,0x00000001,0x00000000}, {0x00000000,0x00000000,0x00000249,0x00000249, 0x00000249,0x00000249,0x00000049,0x00000001}, {0x00000000,0x00000000,0x00000249,0x00000249, 0x00000249,0x0000124a,0x00001252,0x00000001}, {0x00000000,0x00000000,0x00000249,0x00000249, 0x0000124a,0x0000124a,0x00001252,0x00000009}, {0x00000000,0x00000000,0x00000249,0x00001249, 0x0000124a,0x00009252,0x00009292,0x00000009}, {0x00000000,0x00000000,0x00001249,0x00001249, 0x0000124a,0x00009252,0x00009292,0x00000049}, {0x00000000,0x00000000,0x00001249,0x00001249, 0x00009252,0x00009252,0x00009292,0x00000049}, {0x00000000,0x00000000,0x00001249,0x00001249, 0x00009252,0x00009493,0x00009292,0x0000024a}, {0x00000000,0x00000000,0x00001249,0x00001249, 0x00009252,0x00009493,0x00009292,0x0000024a}, {0x00000000,0x00000000,0x00001249,0x00001249, 0x00009252,0x00009493,0x00009493,0x00001252}, {0x00000000,0x00000000,0x00001249,0x0000924a, 0x00009292,0x00009493,0x00009493,0x00001252}, {0x00000000,0x00000000,0x00001249,0x0000924a, 0x00009492,0x00009493,0x00009493,0x00009292}, {0x00000000,0x00000000,0x00001249,0x00009252, 0x00009492,0x0000a49b,0x00009493,0x00009292}, {0x00000000,0x00000000,0x0000924a,0x00009292, 0x0000a493,0x000124db,0x0000a49b,0x00009493}, {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000} } }, { /* version 4 */ { /* version 4, passes 0 */ {0x00000000,0x00000000,0x00000049,0x00000049, 0x00000049,0x00000049,0x0000024a,0x0000024a}, {0x00000000,0x00000000,0x00000249,0x00000249, 0x00000249,0x0000124a,0x00001252,0x00009292}, {0x00000000,0x00000000,0x00000249,0x00000249, 0x0000124a,0x00009252,0x00009292,0x00009292}, {0x00000000,0x00000000,0x00000249,0x00001249, 0x0000124a,0x00009292,0x00009493,0x00009493}, {0x00000000,0x00000000,0x00000249,0x00001249, 0x00009252,0x00009493,0x00009493,0x0000a49b}, {0x00000000,0x00000000,0x00000249,0x0000924a, 0x00009292,0x00009493,0x0000a49b,0x0000a49b}, {0x00000000,0x00000000,0x00001249,0x0000924a, 0x00009292,0x00009493,0x0000a49b,0x000124db}, {0x00000000,0x00000000,0x00001249,0x00009252, 0x00009492,0x0000a49b,0x0000a49b,0x000124db}, {0x00000000,0x00000000,0x00001249,0x00009292, 0x00009492,0x000124db,0x000124db,0x000126dc}, {0x00000000,0x00000000,0x00001249,0x00009292, 0x0000a493,0x000124db,0x000126dc,0x000126dc}, {0x00000000,0x00000000,0x00001249,0x00009493, 0x0000a493,0x000124db,0x000126dc,0x000136e4}, {0x00000000,0x00000000,0x00001249,0x00009493, 0x0000a493,0x000126dc,0x000136e4,0x000136e4}, {0x00000000,0x00000000,0x0000924a,0x00009493, 0x0001249b,0x000126dc,0x000136e4,0x000136e4}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x000124db,0x000136e4,0x000136e4,0x0001b724}, {0x00000000,0x00000000,0x00009252,0x000124db, 0x000126dc,0x0001b724,0x0001b725,0x0001b925}, {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000} }, { /* version 4, passes 1 */ {0x00000000,0x00000000,0x00000049,0x00000049, 0x00000049,0x00000049,0x00000049,0x00000049}, {0x00000000,0x00000000,0x00000249,0x00000249, 0x00000249,0x00000249,0x0000024a,0x00000049}, {0x00000000,0x00000000,0x00001249,0x00000249, 0x0000124a,0x0000124a,0x00001252,0x00000049}, {0x00000000,0x00000000,0x00001249,0x00001249, 0x0000124a,0x0000124a,0x00009292,0x0000024a}, {0x00000000,0x00000000,0x00001249,0x00001249, 0x00009252,0x00009292,0x00009292,0x0000024a}, {0x00000000,0x00000000,0x00001249,0x00001249, 0x00009252,0x00009292,0x0000a49b,0x0000024a}, {0x00000000,0x00000000,0x00001249,0x00001249, 0x00009292,0x00009493,0x0000a49b,0x00001252}, {0x00000000,0x00000000,0x00001249,0x00001249, 0x00009292,0x00009493,0x0000a49b,0x00001252}, {0x00000000,0x00000000,0x00001249,0x0000924a, 0x00009492,0x0000a49b,0x0000a49b,0x00001252}, {0x00000000,0x00000000,0x00001249,0x00009252, 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, {0x00000000,0x00000000,0x00001249,0x00009292, 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, {0x00000000,0x00000000,0x00001249,0x00009493, 0x0000a493,0x0000a49b,0x0000a49b,0x00009292}, {0x00000000,0x00000000,0x00001249,0x00009493, 0x0000a493,0x0000a49b,0x0000a49b,0x00009493}, {0x00000000,0x00000000,0x0000924a,0x00009493, 0x0000a493,0x000124db,0x0000a49b,0x00009493}, {0x00000000,0x00000000,0x00009252,0x0000a49b, 0x0001249b,0x000126dc,0x000124db,0x0000a49b}, {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000} } }, { /* version 5 */ { /* version 5, passes 0 */ {0x00000000,0x00000000,0x00000249,0x00000249, 0x00000249,0x0000124a,0x00001252,0x00009292}, {0x00000000,0x00000000,0x00000249,0x00001249, 0x0000124a,0x00009292,0x00009292,0x00009493}, {0x00000000,0x00000000,0x00000249,0x0000924a, 0x00009292,0x00009493,0x0000a49b,0x0000a49b}, {0x00000000,0x00000000,0x00001249,0x0000924a, 0x00009292,0x00009493,0x0000a49b,0x0000a49b}, {0x00000000,0x00000000,0x00001249,0x0000924a, 0x00009492,0x0000a49b,0x0000a49b,0x000124db}, {0x00000000,0x00000000,0x00001249,0x00009292, 0x00009492,0x0000a49b,0x000124db,0x000124db}, {0x00000000,0x00000000,0x00001249,0x00009292, 0x0000a493,0x000124db,0x000124db,0x000126dc}, {0x00000000,0x00000000,0x00001249,0x00009493, 0x0000a493,0x000124db,0x000126dc,0x000126dc}, {0x00000000,0x00000000,0x00001249,0x00009493, 0x0000a493,0x000126dc,0x000136e4,0x000136e4}, {0x00000000,0x00000000,0x00001249,0x00009493, 0x0001249b,0x000126dc,0x000136e4,0x000136e4}, {0x00000000,0x00000000,0x00001249,0x00009493, 0x0001249b,0x000126dc,0x000136e4,0x000136e4}, {0x00000000,0x00000000,0x0000924a,0x00009493, 0x0001249b,0x000126dc,0x0001b725,0x0001b724}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x000124db,0x000126dc,0x0001b725,0x0001b724}, {0x00000000,0x00000000,0x00009292,0x0000a49b, 0x000126dc,0x000136e4,0x0001b92d,0x0001b925}, {0x00000000,0x00000000,0x00009492,0x000124db, 0x000136e4,0x0001b724,0x0001c96e,0x0001c92d}, {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000} }, { /* version 5, passes 1 */ {0x00000000,0x00000000,0x00000249,0x00000249, 0x0000124a,0x00000249,0x0000024a,0x0000024a}, {0x00000000,0x00000000,0x00001249,0x00001249, 0x0000124a,0x0000124a,0x00001252,0x0000024a}, {0x00000000,0x00000000,0x00001249,0x00001249, 0x00009292,0x00009493,0x00009493,0x0000024a}, {0x00000000,0x00000000,0x00001249,0x00001249, 0x00009292,0x00009493,0x00009493,0x00001252}, {0x00000000,0x00000000,0x00001249,0x00001249, 0x00009292,0x00009493,0x0000a49b,0x00001252}, {0x00000000,0x00000000,0x00001249,0x0000924a, 0x00009492,0x00009493,0x000124db,0x00001252}, {0x00000000,0x00000000,0x00001249,0x00009292, 0x00009492,0x00009493,0x000124db,0x00009292}, {0x00000000,0x00000000,0x00001249,0x00009292, 0x00009492,0x0000a49b,0x000124db,0x00009292}, {0x00000000,0x00000000,0x00001249,0x00009493, 0x0000a493,0x0000a49b,0x000124db,0x00009292}, {0x00000000,0x00000000,0x00001249,0x00009493, 0x0000a493,0x000124db,0x000124db,0x00009493}, {0x00000000,0x00000000,0x0000924a,0x00009493, 0x0000a493,0x000124db,0x000124db,0x00009493}, {0x00000000,0x00000000,0x0000924a,0x00009493, 0x0000a493,0x000124db,0x000124db,0x00009493}, {0x00000000,0x00000000,0x0000924a,0x00009493, 0x0000a493,0x000124db,0x000124db,0x0000a49b}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x000124db,0x000126dc,0x000124db,0x0000a49b}, {0x00000000,0x00000000,0x00009252,0x000124db, 0x000126dc,0x000136e4,0x000126dc,0x000124db}, {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000} } }, { /* version 6 */ { /* version 6, passes 0 */ {0x00000000,0x00000000,0x00000249,0x00000249, 0x0000124a,0x0000124a,0x00009292,0x00009292}, {0x00000000,0x00000000,0x00001249,0x00001249, 0x00009292,0x00009493,0x0000a49b,0x0000a49b}, {0x00000000,0x00000000,0x00001249,0x0000924a, 0x00009492,0x0000a49b,0x0000a49b,0x000124db}, {0x00000000,0x00000000,0x00001249,0x00009292, 0x00009492,0x000124db,0x000126dc,0x000126dc}, {0x00000000,0x00000000,0x00001249,0x00009493, 0x0000a493,0x000124db,0x000126dc,0x000126dc}, {0x00000000,0x00000000,0x00001249,0x00009493, 0x0000a493,0x000126dc,0x000136e4,0x000136e4}, {0x00000000,0x00000000,0x00001249,0x00009493, 0x0000a493,0x000126dc,0x000136e4,0x0001b724}, {0x00000000,0x00000000,0x00001249,0x00009493, 0x0001249b,0x000126dc,0x000136e4,0x0001b724}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0001249b,0x000126dc,0x000136e4,0x0001b724}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0001249b,0x000136e4,0x0001b725,0x0001b724}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x000124db,0x000136e4,0x0001b725,0x0001b925}, {0x00000000,0x00000000,0x00009292,0x0000a49b, 0x000126dc,0x000136e4,0x0001b92d,0x0001b925}, {0x00000000,0x00000000,0x00009292,0x0000a49b, 0x000126dc,0x0001b724,0x0001b92d,0x0001c92d}, {0x00000000,0x00000000,0x00009492,0x000124db, 0x000126dc,0x0001b724,0x0001c96e,0x0001c92d}, {0x00000000,0x00000000,0x0000a492,0x000126db, 0x000136e4,0x0001b925,0x00025bb6,0x00024b77}, {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000} }, { /* version 6, passes 1 */ {0x00000000,0x00000000,0x00001249,0x00000249, 0x0000124a,0x0000124a,0x00001252,0x00001252}, {0x00000000,0x00000000,0x00001249,0x00001249, 0x00009252,0x00009292,0x00009292,0x00001252}, {0x00000000,0x00000000,0x00001249,0x0000924a, 0x00009492,0x00009493,0x0000a49b,0x00001252}, {0x00000000,0x00000000,0x00001249,0x00009252, 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, {0x00000000,0x00000000,0x00001249,0x00009292, 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, {0x00000000,0x00000000,0x00001249,0x00009493, 0x0000a493,0x0000a49b,0x000126dc,0x00009292}, {0x00000000,0x00000000,0x0000924a,0x00009493, 0x0000a493,0x0000a49b,0x000126dc,0x00009493}, {0x00000000,0x00000000,0x0000924a,0x00009493, 0x0000a493,0x0000a49b,0x000126dc,0x00009493}, {0x00000000,0x00000000,0x0000924a,0x00009493, 0x0000a493,0x000124db,0x000126dc,0x00009493}, {0x00000000,0x00000000,0x0000924a,0x00009493, 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0001249b,0x000126dc,0x000126dc,0x0000a49b}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x000124db,0x000136e4,0x000126dc,0x000124db}, {0x00000000,0x00000000,0x00009492,0x0000a49b, 0x000136e4,0x000136e4,0x000126dc,0x000124db}, {0x00000000,0x00000000,0x0000a492,0x000124db, 0x0001b724,0x0001b724,0x000136e4,0x000126dc}, {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000} } }, { /* version 7 */ { /* version 7, passes 0 */ {0x00000000,0x00000000,0x00001249,0x00001249, 0x00009292,0x00009493,0x0000a49b,0x000124db}, {0x00000000,0x00000000,0x00001249,0x00009292, 0x0000a493,0x0000a49b,0x000124db,0x000126dc}, {0x00000000,0x00000000,0x00001249,0x00009493, 0x0000a493,0x000124db,0x000126dc,0x000136e4}, {0x00000000,0x00000000,0x00001249,0x00009493, 0x0000a493,0x000124db,0x000136e4,0x000136e4}, {0x00000000,0x00000000,0x00001249,0x00009493, 0x0001249b,0x000126dc,0x000136e4,0x000136e4}, {0x00000000,0x00000000,0x00001249,0x0000a49b, 0x0001249b,0x000126dc,0x000136e4,0x0001b724}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0001249b,0x000126dc,0x000136e4,0x0001b724}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x000124db,0x000136e4,0x0001b725,0x0001b724}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x000126dc,0x000136e4,0x0001b725,0x0001b925}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x000126dc,0x0001b724,0x0001b92d,0x0001b925}, {0x00000000,0x00000000,0x00009292,0x0000a49b, 0x000126dc,0x0001b724,0x0001c96e,0x0001c92d}, {0x00000000,0x00000000,0x00009292,0x000124db, 0x000126dc,0x0001b724,0x0001c96e,0x0001c92d}, {0x00000000,0x00000000,0x00009492,0x000124db, 0x000136e4,0x0001b724,0x0001c96e,0x0002496e}, {0x00000000,0x00000000,0x00009492,0x000126db, 0x000136e4,0x0001b925,0x0001c96e,0x0002496e}, {0x00000000,0x00000000,0x0000a492,0x000136db, 0x0001b724,0x0002496d,0x00025bb6,0x00025bbf}, {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000} }, { /* version 7, passes 1 */ {0x00000000,0x00000000,0x00001249,0x00001249, 0x00009252,0x00009292,0x00009292,0x00009292}, {0x00000000,0x00000000,0x00001249,0x0000924a, 0x00009492,0x00009493,0x00009493,0x00009292}, {0x00000000,0x00000000,0x00001249,0x00009493, 0x0000a493,0x0000a49b,0x0000a49b,0x00009292}, {0x00000000,0x00000000,0x0000924a,0x00009493, 0x0000a493,0x0000a49b,0x000124db,0x00009493}, {0x00000000,0x00000000,0x0000924a,0x00009493, 0x0000a493,0x000124db,0x000124db,0x00009493}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0000a493,0x000124db,0x000136e4,0x00009493}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0000a493,0x000124db,0x000136e4,0x0000a49b}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0001249b,0x000124db,0x000136e4,0x0000a49b}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0001249b,0x000126dc,0x000136e4,0x0000a49b}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0001249b,0x000126dc,0x000136e4,0x000124db}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x000126dc,0x000136e4,0x000136e4,0x000124db}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x000126dc,0x000136e4,0x000136e4,0x000124db}, {0x00000000,0x00000000,0x0000924a,0x000124db, 0x000136e4,0x000136e4,0x000136e4,0x000126dc}, {0x00000000,0x00000000,0x0000a492,0x000124db, 0x000136e4,0x0001b724,0x000136e4,0x000126dc}, {0x00000000,0x00000000,0x00012492,0x000126db, 0x0001b724,0x0001b925,0x0001b725,0x000136e4}, {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000} } }, { /* version 8 */ { /* version 8, passes 0 */ {0x00000000,0x00000000,0x00001249,0x00001249, 0x00009292,0x00009493,0x0000a49b,0x000124db}, {0x00000000,0x00000000,0x00001249,0x00009292, 0x0000a493,0x000124db,0x000126dc,0x000126dc}, {0x00000000,0x00000000,0x00001249,0x00009493, 0x0000a493,0x000124db,0x000126dc,0x000136e4}, {0x00000000,0x00000000,0x00001249,0x0000a49b, 0x0001249b,0x000126dc,0x000136e4,0x0001b724}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0001249b,0x000126dc,0x000136e4,0x0001b724}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x000124db,0x000136e4,0x0001b725,0x0001b724}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x000126dc,0x000136e4,0x0001b725,0x0001b925}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x000126dc,0x0001b724,0x0001b92d,0x0001c92d}, {0x00000000,0x00000000,0x00009252,0x000124db, 0x000126dc,0x0001b724,0x0001b92d,0x0001c92d}, {0x00000000,0x00000000,0x00009292,0x000124db, 0x000126dc,0x0001b925,0x0001c96e,0x0001c92d}, {0x00000000,0x00000000,0x00009492,0x000124db, 0x000136e4,0x0001b925,0x0001c96e,0x0001c92d}, {0x00000000,0x00000000,0x00009492,0x000124db, 0x000136e4,0x0001b925,0x00024b76,0x00024b77}, {0x00000000,0x00000000,0x00009492,0x000126db, 0x000136e4,0x0001b925,0x00024b76,0x00025bbf}, {0x00000000,0x00000000,0x0000a492,0x000126db, 0x000136e4,0x0001c92d,0x00024b76,0x00025bbf}, {0x00000000,0x00000000,0x00012492,0x000136db, 0x0001b724,0x00024b6d,0x0002ddb6,0x0002efff}, {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000} }, { /* version 8, passes 1 */ {0x00000000,0x00000000,0x00001249,0x00001249, 0x00009252,0x00009493,0x00009493,0x00009493}, {0x00000000,0x00000000,0x00001249,0x00009292, 0x0000a493,0x0000a49b,0x0000a49b,0x00009493}, {0x00000000,0x00000000,0x0000924a,0x00009493, 0x0000a493,0x0000a49b,0x000124db,0x00009493}, {0x00000000,0x00000000,0x0000924a,0x00009493, 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0000a493,0x000124db,0x000136e4,0x000124db}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0001249b,0x000126dc,0x000136e4,0x000124db}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x000126dc,0x000126dc,0x000136e4,0x000126dc}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x000126dc,0x000136e4,0x000136e4,0x000126dc}, {0x00000000,0x00000000,0x0000924a,0x000124db, 0x000126dc,0x000136e4,0x000136e4,0x000126dc}, {0x00000000,0x00000000,0x0000924a,0x000124db, 0x000126dc,0x000136e4,0x000136e4,0x000136e4}, {0x00000000,0x00000000,0x00009292,0x000124db, 0x000136e4,0x0001b724,0x0001b725,0x000136e4}, {0x00000000,0x00000000,0x00009492,0x000126db, 0x000136e4,0x0001b925,0x0001b725,0x0001b724}, {0x00000000,0x00000000,0x00009492,0x000126db, 0x000136e4,0x0001b925,0x0001b725,0x0001b724}, {0x00000000,0x00000000,0x0000a492,0x000136db, 0x0001b724,0x0002496d,0x0001b92d,0x0001b925}, {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000} } }, { /* version 9 */ { /* version 9, passes 0 */ {0x00000000,0x00000000,0x00000049,0x00000049, 0x00000049,0x00000049,0x00000049,0x00000049}, {0x00000000,0x00000000,0x00000249,0x00000049, 0x00000249,0x00000249,0x0000024a,0x00000049}, {0x00000000,0x00000000,0x00000249,0x00000249, 0x0000124a,0x00009252,0x00001252,0x0000024a}, {0x00000000,0x00000000,0x00001249,0x00001249, 0x00009252,0x00009292,0x00009493,0x00001252}, {0x00000000,0x00000000,0x00001249,0x0000924a, 0x00009292,0x00009493,0x00009493,0x00001252}, {0x00000000,0x00000000,0x00001249,0x00009292, 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, {0x00000000,0x00000000,0x00001249,0x00009493, 0x0000a493,0x000124db,0x000124db,0x00009493}, {0x00000000,0x00000000,0x0000924a,0x00009493, 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, {0x00000000,0x00000000,0x0000924a,0x00009493, 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, {0x00000000,0x00000000,0x0000924a,0x00009493, 0x0001249b,0x000126dc,0x000126dc,0x000124db}, {0x00000000,0x00000000,0x00009252,0x00009493, 0x000124db,0x000136e4,0x000136e4,0x000126dc}, {0x00000000,0x00000000,0x00009252,0x0000a49b, 0x000124db,0x000136e4,0x000136e4,0x000126dc}, {0x00000000,0x00000000,0x00009292,0x0000a49b, 0x000126dc,0x000136e4,0x000136e4,0x000136e4}, {0x00000000,0x00000000,0x00009492,0x0000a49b, 0x000126dc,0x0001b724,0x0001b725,0x0001b724}, {0x00000000,0x00000000,0x0000a492,0x000124db, 0x000136e4,0x0001b925,0x0001b92d,0x0001b925}, {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000} }, { /* version 9, passes 1 */ {0x00000000,0x00000000,0x00000249,0x00000049, 0x00000009,0x00000009,0x00000009,0x00000009}, {0x00000000,0x00000000,0x00000249,0x00000249, 0x00000049,0x00000049,0x00000009,0x00000009}, {0x00000000,0x00000000,0x00001249,0x00001249, 0x0000124a,0x00000249,0x00000049,0x00000049}, {0x00000000,0x00000000,0x00001249,0x00001249, 0x0000124a,0x0000124a,0x00000049,0x00000049}, {0x00000000,0x00000000,0x00001249,0x00001249, 0x00009252,0x0000124a,0x0000024a,0x0000024a}, {0x00000000,0x00000000,0x00001249,0x0000924a, 0x00009252,0x0000124a,0x0000024a,0x0000024a}, {0x00000000,0x00000000,0x00001249,0x00009292, 0x00009492,0x00009252,0x00001252,0x00001252}, {0x00000000,0x00000000,0x00001249,0x00009493, 0x0000a493,0x00009292,0x00009292,0x00001252}, {0x00000000,0x00000000,0x0000924a,0x00009493, 0x0000a493,0x00009292,0x00009292,0x00009292}, {0x00000000,0x00000000,0x0000924a,0x00009493, 0x0000a493,0x00009493,0x00009493,0x00009292}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0000a493,0x0000a49b,0x00009493,0x00009493}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0000a493,0x0000a49b,0x0000a49b,0x00009493}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0001249b,0x000124db,0x0000a49b,0x0000a49b}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, {0x00000000,0x00000000,0x00009252,0x000124db, 0x0001b724,0x000136e4,0x000126dc,0x000124db}, {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000} } }, { /* version 10 */ { /* version 10, passes 0 */ {0x00000000,0x00000000,0x00000249,0x00000249, 0x00000249,0x00000249,0x0000024a,0x0000024a}, {0x00000000,0x00000000,0x00000249,0x00001249, 0x00009252,0x00009292,0x00009292,0x0000024a}, {0x00000000,0x00000000,0x00001249,0x00001249, 0x00009252,0x00009292,0x00009292,0x00001252}, {0x00000000,0x00000000,0x00001249,0x0000924a, 0x00009492,0x00009493,0x0000a49b,0x00009292}, {0x00000000,0x00000000,0x00001249,0x00009292, 0x00009492,0x000124db,0x000124db,0x00009292}, {0x00000000,0x00000000,0x00001249,0x00009493, 0x0000a493,0x000124db,0x000124db,0x00009493}, {0x00000000,0x00000000,0x00001249,0x00009493, 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, {0x00000000,0x00000000,0x0000924a,0x00009493, 0x0000a493,0x000124db,0x000126dc,0x000124db}, {0x00000000,0x00000000,0x0000924a,0x00009493, 0x0001249b,0x000126dc,0x000126dc,0x000124db}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x000124db,0x000126dc,0x000136e4,0x000126dc}, {0x00000000,0x00000000,0x00009252,0x0000a49b, 0x000124db,0x000136e4,0x000136e4,0x000136e4}, {0x00000000,0x00000000,0x00009292,0x0000a49b, 0x000126dc,0x000136e4,0x000136e4,0x000136e4}, {0x00000000,0x00000000,0x00009492,0x0000a49b, 0x000126dc,0x0001b724,0x0001b92d,0x0001b724}, {0x00000000,0x00000000,0x00009492,0x000124db, 0x000126dc,0x0001b925,0x0001b92d,0x0001b925}, {0x00000000,0x00000000,0x0000a492,0x000126db, 0x000136e4,0x0002496d,0x0001c96e,0x0001c92d}, {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000} }, { /* version 10, passes 1 */ {0x00000000,0x00000000,0x00000249,0x00000249, 0x00000049,0x00000049,0x00000049,0x00000049}, {0x00000000,0x00000000,0x00001249,0x00001249, 0x0000124a,0x00000249,0x00000049,0x00000049}, {0x00000000,0x00000000,0x00001249,0x00001249, 0x0000124a,0x00009252,0x0000024a,0x00000049}, {0x00000000,0x00000000,0x00001249,0x00001249, 0x00009252,0x00009493,0x0000024a,0x0000024a}, {0x00000000,0x00000000,0x00001249,0x00009252, 0x00009492,0x00009493,0x00001252,0x0000024a}, {0x00000000,0x00000000,0x00001249,0x00009292, 0x00009492,0x00009493,0x00001252,0x00001252}, {0x00000000,0x00000000,0x0000924a,0x00009493, 0x00009492,0x00009493,0x00009292,0x00001252}, {0x00000000,0x00000000,0x0000924a,0x00009493, 0x0000a493,0x00009493,0x00009292,0x00009292}, {0x00000000,0x00000000,0x0000924a,0x00009493, 0x0000a493,0x0000a49b,0x00009493,0x00009292}, {0x00000000,0x00000000,0x0000924a,0x00009493, 0x0000a493,0x0000a49b,0x00009493,0x00009292}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0000a493,0x000124db,0x0000a49b,0x00009493}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0000a493,0x000124db,0x0000a49b,0x00009493}, {0x00000000,0x00000000,0x0000924a,0x000124db, 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, {0x00000000,0x00000000,0x0000924a,0x000124db, 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, {0x00000000,0x00000000,0x00009252,0x000126db, 0x0001b724,0x000136e4,0x000126dc,0x000124db}, {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000} } }, { /* version 11 */ { /* version 11, passes 0 */ {0x00000000,0x00000000,0x00000249,0x00000249, 0x00000249,0x00000249,0x00001252,0x00001252}, {0x00000000,0x00000000,0x00001249,0x00001249, 0x00009252,0x00009292,0x00009292,0x00001252}, {0x00000000,0x00000000,0x00001249,0x0000924a, 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, {0x00000000,0x00000000,0x00001249,0x00009493, 0x0000a493,0x0000a49b,0x000124db,0x00009493}, {0x00000000,0x00000000,0x00001249,0x00009493, 0x0000a493,0x000124db,0x000126dc,0x00009493}, {0x00000000,0x00000000,0x0000924a,0x00009493, 0x0000a493,0x000126dc,0x000126dc,0x0000a49b}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0001249b,0x000126dc,0x000136e4,0x000124db}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x000126dc,0x000136e4,0x000136e4,0x000126dc}, {0x00000000,0x00000000,0x00009292,0x0000a49b, 0x000126dc,0x000136e4,0x000136e4,0x000126dc}, {0x00000000,0x00000000,0x00009292,0x0000a49b, 0x000126dc,0x0001b724,0x0001b725,0x000136e4}, {0x00000000,0x00000000,0x00009292,0x0000a49b, 0x000136e4,0x0001b724,0x0001b92d,0x0001b724}, {0x00000000,0x00000000,0x00009492,0x0000a49b, 0x000136e4,0x0001b724,0x0001b92d,0x0001b724}, {0x00000000,0x00000000,0x00009492,0x000124db, 0x000136e4,0x0001b925,0x0001c96e,0x0001b925}, {0x00000000,0x00000000,0x00009492,0x000124db, 0x0001b724,0x0001b925,0x0001c96e,0x0001c92d}, {0x00000000,0x00000000,0x0000a492,0x000126db, 0x0001c924,0x0002496d,0x00025bb6,0x00024b77}, {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000} }, { /* version 11, passes 1 */ {0x00000000,0x00000000,0x00001249,0x00000249, 0x00000249,0x00000249,0x0000024a,0x0000024a}, {0x00000000,0x00000000,0x00001249,0x00001249, 0x0000124a,0x0000124a,0x0000024a,0x0000024a}, {0x00000000,0x00000000,0x00001249,0x0000924a, 0x00009252,0x00009252,0x0000024a,0x0000024a}, {0x00000000,0x00000000,0x00001249,0x00009292, 0x00009492,0x0000a49b,0x00001252,0x00001252}, {0x00000000,0x00000000,0x0000924a,0x00009493, 0x0000a493,0x0000a49b,0x00001252,0x00001252}, {0x00000000,0x00000000,0x0000924a,0x00009493, 0x0000a493,0x0000a49b,0x00009292,0x00001252}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0000a493,0x0000a49b,0x00009292,0x00009292}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0000a493,0x0000a49b,0x00009493,0x00009292}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0001249b,0x000124db,0x00009493,0x00009292}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0001249b,0x000124db,0x00009493,0x00009493}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x000124db,0x000124db,0x0000a49b,0x00009493}, {0x00000000,0x00000000,0x0000924a,0x000124db, 0x000126dc,0x000126dc,0x0000a49b,0x00009493}, {0x00000000,0x00000000,0x0000924a,0x000124db, 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, {0x00000000,0x00000000,0x00009292,0x000124db, 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, {0x00000000,0x00000000,0x00009492,0x000126db, 0x0001b724,0x000136e4,0x000126dc,0x000124db}, {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000} } }, { /* version 12 */ { /* version 12, passes 0 */ {0x00000000,0x00000000,0x00001249,0x00001249, 0x00009252,0x00009292,0x00009493,0x00009493}, {0x00000000,0x00000000,0x00001249,0x00009292, 0x0000a493,0x0000a49b,0x0000a49b,0x00009493}, {0x00000000,0x00000000,0x00001249,0x00009493, 0x0000a493,0x000124db,0x000124db,0x0000a49b}, {0x00000000,0x00000000,0x0000924a,0x00009493, 0x0000a493,0x000126dc,0x000126dc,0x0000a49b}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0001249b,0x000126dc,0x000136e4,0x000124db}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x000126dc,0x000136e4,0x000136e4,0x000126dc}, {0x00000000,0x00000000,0x00009292,0x0000a49b, 0x000126dc,0x0001b724,0x0001b725,0x000126dc}, {0x00000000,0x00000000,0x00009292,0x0000a49b, 0x000136e4,0x0001b724,0x0001b92d,0x000136e4}, {0x00000000,0x00000000,0x00009492,0x0000a49b, 0x000136e4,0x0001b724,0x0001b92d,0x0001b724}, {0x00000000,0x00000000,0x00009492,0x000124db, 0x000136e4,0x0001b724,0x0001b92d,0x0001b724}, {0x00000000,0x00000000,0x00009492,0x000124db, 0x000136e4,0x0001b925,0x0001b92d,0x0001b925}, {0x00000000,0x00000000,0x00009492,0x000124db, 0x0001b724,0x0001b925,0x0001c96e,0x0001c92d}, {0x00000000,0x00000000,0x0000a492,0x000124db, 0x0001b724,0x0001c92d,0x0001c96e,0x0001c92d}, {0x00000000,0x00000000,0x0000a492,0x000124db, 0x0001b724,0x0001c92d,0x00024b76,0x0002496e}, {0x00000000,0x00000000,0x00012492,0x000126db, 0x0001c924,0x00024b6d,0x0002ddb6,0x00025bbf}, {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000} }, { /* version 12, passes 1 */ {0x00000000,0x00000000,0x00001249,0x00001249, 0x0000124a,0x0000124a,0x00001252,0x00001252}, {0x00000000,0x00000000,0x00001249,0x00009292, 0x00009492,0x00009252,0x00001252,0x00001252}, {0x00000000,0x00000000,0x0000924a,0x00009493, 0x0000a493,0x00009292,0x00001252,0x00001252}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0000a493,0x0000a49b,0x00009292,0x00009292}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0000a493,0x0000a49b,0x00009292,0x00009292}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0001249b,0x0000a49b,0x00009493,0x00009292}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x000124db,0x000124db,0x00009493,0x00009493}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x000124db,0x000124db,0x0000a49b,0x00009493}, {0x00000000,0x00000000,0x0000924a,0x000124db, 0x000126dc,0x000124db,0x0000a49b,0x00009493}, {0x00000000,0x00000000,0x0000924a,0x000124db, 0x000126dc,0x000126dc,0x0000a49b,0x0000a49b}, {0x00000000,0x00000000,0x0000924a,0x000124db, 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, {0x00000000,0x00000000,0x00009492,0x000126db, 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, {0x00000000,0x00000000,0x00009492,0x000126db, 0x0001b724,0x000136e4,0x000126dc,0x000124db}, {0x00000000,0x00000000,0x00009492,0x000126db, 0x0001b724,0x000136e4,0x000126dc,0x000124db}, {0x00000000,0x00000000,0x0000a492,0x000136db, 0x0001c924,0x0001b724,0x000136e4,0x000126dc}, {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000} } }, { /* version 13 */ { /* version 13, passes 0 */ {0x00000000,0x00000000,0x00001249,0x00001249, 0x00009252,0x00009292,0x00009493,0x00009493}, {0x00000000,0x00000000,0x00001249,0x00009493, 0x0000a493,0x000124db,0x000126dc,0x00009493}, {0x00000000,0x00000000,0x00001249,0x0000a49b, 0x0001249b,0x000126dc,0x000126dc,0x0000a49b}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0001249b,0x000126dc,0x000136e4,0x0000a49b}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x000126dc,0x000136e4,0x0001b725,0x000124db}, {0x00000000,0x00000000,0x00009292,0x0000a49b, 0x000136e4,0x0001b724,0x0001b725,0x000126dc}, {0x00000000,0x00000000,0x00009292,0x000124db, 0x000136e4,0x0001b724,0x0001b725,0x000126dc}, {0x00000000,0x00000000,0x00009492,0x000124db, 0x000136e4,0x0001b724,0x0001c96e,0x000136e4}, {0x00000000,0x00000000,0x00009492,0x000124db, 0x000136e4,0x0001c92d,0x0001c96e,0x0001b724}, {0x00000000,0x00000000,0x0000a492,0x000124db, 0x000136e4,0x0001c92d,0x0001c96e,0x0001b724}, {0x00000000,0x00000000,0x0000a492,0x000124db, 0x0001b724,0x0001c92d,0x0001c96e,0x0001b925}, {0x00000000,0x00000000,0x0000a492,0x000126db, 0x0001b724,0x0001c92d,0x00024b76,0x0001c92d}, {0x00000000,0x00000000,0x0000a492,0x000126db, 0x0001b924,0x0001c92d,0x00024b76,0x0001c92d}, {0x00000000,0x00000000,0x0000a492,0x000126db, 0x0001b924,0x0001c92d,0x00024b76,0x0002496e}, {0x00000000,0x00000000,0x00012492,0x000136db, 0x00024924,0x00024b6d,0x0002ddb6,0x00025bbf}, {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000} }, { /* version 13, passes 1 */ {0x00000000,0x00000000,0x00001249,0x00001249, 0x0000124a,0x0000124a,0x00001252,0x00001252}, {0x00000000,0x00000000,0x0000924a,0x00009493, 0x00009492,0x00009292,0x00001252,0x00001252}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0000a493,0x0000a49b,0x00001252,0x00001252}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0000a493,0x0000a49b,0x00009292,0x00009292}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0000a493,0x0000a49b,0x00009292,0x00009292}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x000126dc,0x0000a49b,0x00009493,0x00009292}, {0x00000000,0x00000000,0x0000924a,0x000124db, 0x000126dc,0x000124db,0x00009493,0x00009493}, {0x00000000,0x00000000,0x0000924a,0x000124db, 0x000136e4,0x000124db,0x0000a49b,0x00009493}, {0x00000000,0x00000000,0x0000924a,0x000136db, 0x0001b724,0x000124db,0x0000a49b,0x00009493}, {0x00000000,0x00000000,0x0000924a,0x000136db, 0x0001b724,0x000126dc,0x0000a49b,0x0000a49b}, {0x00000000,0x00000000,0x00009292,0x000136db, 0x0001b724,0x000126dc,0x000124db,0x0000a49b}, {0x00000000,0x00000000,0x00009492,0x000136db, 0x0001b724,0x000126dc,0x000124db,0x0000a49b}, {0x00000000,0x00000000,0x0000a492,0x000136db, 0x0001b724,0x000136e4,0x000126dc,0x000124db}, {0x00000000,0x00000000,0x0000a492,0x000136db, 0x0001b724,0x000136e4,0x000126dc,0x000124db}, {0x00000000,0x00000000,0x00012492,0x0001b6db, 0x0001c924,0x0001b724,0x000136e4,0x000126dc}, {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000} } }, { /* version 14 */ { /* version 14, passes 0 */ {0x00000000,0x00000000,0x00001249,0x0000924a, 0x00009292,0x00009493,0x00009493,0x00009493}, {0x00000000,0x00000000,0x00001249,0x0000a49b, 0x0000a493,0x000124db,0x000126dc,0x00009493}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0001249b,0x000126dc,0x000136e4,0x0000a49b}, {0x00000000,0x00000000,0x0000924a,0x000124db, 0x000126dc,0x000136e4,0x0001b725,0x000124db}, {0x00000000,0x00000000,0x00009292,0x000124db, 0x000126dc,0x0001b724,0x0001b92d,0x000126dc}, {0x00000000,0x00000000,0x00009492,0x000124db, 0x000136e4,0x0001b724,0x0001b92d,0x000126dc}, {0x00000000,0x00000000,0x00009492,0x000124db, 0x000136e4,0x0001c92d,0x0001c96e,0x000136e4}, {0x00000000,0x00000000,0x00009492,0x000124db, 0x0001b724,0x0001c92d,0x0001c96e,0x0001b724}, {0x00000000,0x00000000,0x0000a492,0x000124db, 0x0001b724,0x0001c92d,0x00024b76,0x0001b925}, {0x00000000,0x00000000,0x0000a492,0x000126db, 0x0001b724,0x0001c92d,0x00024b76,0x0001c92d}, {0x00000000,0x00000000,0x0000a492,0x000126db, 0x0001b724,0x0001c92d,0x00024b76,0x0001c92d}, {0x00000000,0x00000000,0x0000a492,0x000136db, 0x0001b724,0x0001c92d,0x00024b76,0x0002496e}, {0x00000000,0x00000000,0x0000a492,0x000136db, 0x0001b924,0x0002496d,0x00024b76,0x00024b77}, {0x00000000,0x00000000,0x0000a492,0x000136db, 0x0001b924,0x00024b6d,0x0002ddb6,0x00025bbf}, {0x00000000,0x00000000,0x00012492,0x0001b6db, 0x00024924,0x0002db6d,0x00036db6,0x0002efff}, {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000} }, { /* version 14, passes 1 */ {0x00000000,0x00000000,0x00001249,0x00001249, 0x0000124a,0x0000124a,0x00001252,0x00001252}, {0x00000000,0x00000000,0x0000924a,0x00009493, 0x0000a493,0x00009292,0x00001252,0x00001252}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0000a493,0x0000a49b,0x00001252,0x00001252}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0001249b,0x000136e4,0x00009292,0x00009292}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0001249b,0x000136e4,0x00009292,0x00009292}, {0x00000000,0x00000000,0x0000924a,0x000124db, 0x000136e4,0x000136e4,0x00009493,0x00009292}, {0x00000000,0x00000000,0x00009492,0x000136db, 0x0001b724,0x000136e4,0x00009493,0x00009493}, {0x00000000,0x00000000,0x00009492,0x000136db, 0x0001b724,0x000136e4,0x0000a49b,0x00009493}, {0x00000000,0x00000000,0x00009492,0x000136db, 0x0001b724,0x000136e4,0x0000a49b,0x00009493}, {0x00000000,0x00000000,0x00009492,0x000136db, 0x0001b724,0x000136e4,0x0000a49b,0x0000a49b}, {0x00000000,0x00000000,0x0000a492,0x000136db, 0x0001b724,0x000136e4,0x000124db,0x0000a49b}, {0x00000000,0x00000000,0x0000a492,0x000136db, 0x0001b724,0x000136e4,0x000124db,0x0000a49b}, {0x00000000,0x00000000,0x0000a492,0x000136db, 0x0001b724,0x000136e4,0x000126dc,0x000124db}, {0x00000000,0x00000000,0x0000a492,0x000136db, 0x0001b724,0x000136e4,0x000126dc,0x000124db}, {0x00000000,0x00000000,0x00012492,0x0001b6db, 0x0001c924,0x0001b724,0x000136e4,0x000126dc}, {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000} } }, { /* version 15 */ { /* version 15, passes 0 */ {0x00000000,0x00000000,0x00001249,0x00009493, 0x0000a493,0x0000a49b,0x000124db,0x000124db}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0001249b,0x000126dc,0x000136e4,0x000124db}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x000126dc,0x0001b724,0x0001b725,0x000126dc}, {0x00000000,0x00000000,0x0000924a,0x000124db, 0x000136e4,0x0001b724,0x0001b92d,0x000126dc}, {0x00000000,0x00000000,0x00009492,0x000124db, 0x000136e4,0x0001b925,0x0001c96e,0x000136e4}, {0x00000000,0x00000000,0x00009492,0x000124db, 0x0001b724,0x0001c92d,0x0001c96e,0x0001b724}, {0x00000000,0x00000000,0x0000a492,0x000124db, 0x0001b724,0x0001c92d,0x0001c96e,0x0001b724}, {0x00000000,0x00000000,0x0000a492,0x000126db, 0x0001b724,0x0001c92d,0x0001c96e,0x0001b925}, {0x00000000,0x00000000,0x0000a492,0x000126db, 0x0001b924,0x0001c92d,0x00024b76,0x0001c92d}, {0x00000000,0x00000000,0x0000a492,0x000136db, 0x0001b924,0x0001c92d,0x00024b76,0x0001c92d}, {0x00000000,0x00000000,0x0000a492,0x000136db, 0x0001b924,0x0002496d,0x00024b76,0x0002496e}, {0x00000000,0x00000000,0x0000a492,0x000136db, 0x0001c924,0x0002496d,0x00025bb6,0x00024b77}, {0x00000000,0x00000000,0x0000a492,0x000136db, 0x0001c924,0x00024b6d,0x00025bb6,0x00024b77}, {0x00000000,0x00000000,0x00012492,0x000136db, 0x0001c924,0x00024b6d,0x0002ddb6,0x00025bbf}, {0x00000000,0x00000000,0x00012492,0x0001b6db, 0x00024924,0x0002db6d,0x00036db6,0x0002efff}, {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000} }, { /* version 15, passes 1 */ {0x00000000,0x00000000,0x0000924a,0x0000924a, 0x00009292,0x00009292,0x00009292,0x00009292}, {0x00000000,0x00000000,0x0000924a,0x0000a49b, 0x0000a493,0x000124db,0x00009292,0x00009292}, {0x00000000,0x00000000,0x0000924a,0x000124db, 0x000124db,0x0001b724,0x00009493,0x00009493}, {0x00000000,0x00000000,0x0000924a,0x000124db, 0x000126dc,0x0001b724,0x00009493,0x00009493}, {0x00000000,0x00000000,0x0000924a,0x000124db, 0x000136e4,0x0001b724,0x0000a49b,0x0000a49b}, {0x00000000,0x00000000,0x00009292,0x000136db, 0x0001b724,0x0001b724,0x0000a49b,0x0000a49b}, {0x00000000,0x00000000,0x00009492,0x000136db, 0x0001c924,0x0001b724,0x000124db,0x000124db}, {0x00000000,0x00000000,0x00009492,0x000136db, 0x0001c924,0x0001b724,0x000124db,0x000124db}, {0x00000000,0x00000000,0x0000a492,0x000136db, 0x0001c924,0x0001b724,0x000126dc,0x000126dc}, {0x00000000,0x00000000,0x0000a492,0x000136db, 0x0001c924,0x0001b925,0x000126dc,0x000126dc}, {0x00000000,0x00000000,0x0000a492,0x000136db, 0x0001c924,0x0001b925,0x000136e4,0x000136e4}, {0x00000000,0x00000000,0x0000a492,0x000136db, 0x0001c924,0x0001b925,0x000136e4,0x000136e4}, {0x00000000,0x00000000,0x0000a492,0x000136db, 0x0001c924,0x0001b925,0x0001b725,0x0001b724}, {0x00000000,0x00000000,0x00012492,0x000136db, 0x0001c924,0x0001b925,0x0001b725,0x0001b724}, {0x00000000,0x00000000,0x00012492,0x0001b6db, 0x00024924,0x0002496d,0x0001b92d,0x0001b925}, {0x00000000,0x00000000,0x00000000,0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000} } } }; pwcbsd/pwcbsd/pwc-timon.h000644 000423 000000 00000004117 10546676137 016170 0ustar00luigiwheel000000 000000 /* * PWCBSD - Philips USB webcam driver for FreeBSD 5.4 and higher * * Copyright (C) 2006 Raaf * * Based on the Linux pwc driver. * * Copyright (C) 1999-2003 Nemosoft Unv. * Copyright (C) 2004-2006 Luc Saillard * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * 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 */ /* This tables contains entries for the 675/680/690 (Timon) camera, with 4 different qualities (no compression, low, medium, high). It lists the bandwidth requirements for said mode by its alternate interface number. An alternate of 0 means that the mode is unavailable. There are 6 * 4 * 4 entries: 6 different resolutions subqcif, qsif, qcif, sif, cif, vga 6 framerates: 5, 10, 15, 20, 25, 30 4 compression modi: none, low, medium, high When an uncompressed mode is not available, the next available compressed mode will be chosen (unless the decompressor is absent). Sometimes there are only 1 or 2 compressed modes available; in that case entries are duplicated. */ #ifndef PWC_TIMON_H #define PWC_TIMON_H #include "pwc-ioctl.h" struct Timon_table_entry { char alternate; /* USB alternate interface */ unsigned short packetsize; /* Normal packet size */ unsigned short bandlength; /* Bandlength when decompressing */ unsigned char mode[13]; /* precomputed mode settings for cam */ }; const extern struct Timon_table_entry Timon_table[PSZ_MAX][6][4]; const extern unsigned int TimonRomTable [16][2][16][8]; #endif pwcbsd/pwcbsd/pwc-uncompress.c000644 000423 000000 00000006503 10552522124 017213 0ustar00luigiwheel000000 000000 /* * PWCBSD - Philips USB webcam driver for FreeBSD 5.4 and higher * * Copyright (C) 2006 Raaf * * Based on the Linux pwc driver. * * Copyright (C) 1999-2003 Nemosoft Unv. * Copyright (C) 2004-2006 Luc Saillard * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * 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 "pwc.h" #include "pwc-uncompress.h" #include "pwc-dec1.h" #include "pwc-dec23.h" int pwc_decompress(struct pwc_softc *pdev, unsigned char *yuv, unsigned char *image, int srclen) { int n, line, col, stride; if (pdev->vbandlength == 0) { u_int16_t *src; u_int16_t *dsty, *dstu, *dstv; /* Uncompressed mode. We copy the data into the output buffer, using the viewport size (which may be larger than the image size). Unfortunately we have to do a bit of byte stuffing to get the desired output format/size. */ /* * We do some byte shuffling here to go from the * native format to YUV420P. */ src = (u_int16_t *)yuv; n = pdev->view.x * pdev->view.y; /* offset in Y plane */ stride = pdev->view.x * pdev->offset.y + pdev->offset.x; dsty = (u_int16_t *)(image + stride); /* offsets in U/V planes */ stride = pdev->view.x * pdev->offset.y / 4 + pdev->offset.x / 2; dstu = (u_int16_t *)(image + n + stride); dstv = (u_int16_t *)(image + n + n / 4 + stride); /* increment after each line */ stride = (pdev->view.x - pdev->image.x) / 2; /* u16 is 2 bytes */ for (line = 0; line < pdev->image.y; line++) { for (col = 0; col < pdev->image.x; col += 4) { *dsty++ = *src++; *dsty++ = *src++; if (line & 1) *dstv++ = *src++; else *dstu++ = *src++; } dsty += stride; if (line & 1) dstv += (stride >> 1); else dstu += (stride >> 1); } } else { /* Compressed; the decompressor routines will write the data in planar format immediately. */ int flags; flags = PWCX_FLAG_PLANAR; if (pdev->vsize == PSZ_VGA && pdev->vframes == 5 && pdev->vsnapshot) { printf("pwc: Mode Bayer is not supported for now\n"); flags |= PWCX_FLAG_BAYER; return -ENXIO; /* No such device or address: missing decompressor */ } switch (pdev->pwc_info.type) { case 675: case 680: case 690: case 720: case 730: case 740: case 750: pwc_dec23_decompress(&pdev->image, &pdev->view, &pdev->offset, yuv, image, flags, pdev->decompress_data, pdev->vbandlength); break; case 645: case 646: /* TODO & FIXME */ printf("pwc: This chipset is not supported for now\n"); return -ENXIO; /* No such device or address: missing decompressor */ break; } } return 0; } pwcbsd/pwcbsd/pwc-v4l.c000644 000423 000000 00000023742 10552522125 015527 0ustar00luigiwheel000000 000000 /* * PWCBSD - Philips USB webcam driver for FreeBSD 5.4 and higher * * Copyright (C) 2006 Raaf * * Based on the Linux pwc driver. * * Copyright (C) 1999-2003 Nemosoft Unv. * Copyright (C) 2004-2006 Luc Saillard * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * 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 "pwc.h" #include "pwc-ioctl.h" #ifdef USB_DEBUG static const char *v4l1_ioctls[] = { "?", "CGAP", "GCHAN", "SCHAN", "GTUNER", "STUNER", "GPICT", "SPICT", "CCAPTURE", "GWIN", "SWIN", "GFBUF", "SFBUF", "KEY", "GFREQ", "SFREQ", "GAUDIO", "SAUDIO", "SYNC", "MCAPTURE", "GMBUF", "GUNIT", "GCAPTURE", "SCAPTURE", "SPLAYMODE", "SWRITEMODE", "GPLAYINFO", "SMICROCODE", "GVBIFMT", "SVBIFMT" }; #define V4L1_IOCTLS (sizeof(v4l1_ioctls)/ sizeof(v4l1_ioctls[0])) #define _IOC_TYPEBITS 8 #define _IOC_NRBITS 8 #define _IOC_NRSHIFT 0 #define _IOC_NRMASK ((1 << _IOC_NRBITS)-1) #define _IOC_TYPEMASK ((1 << _IOC_TYPEBITS)-1) #define _IOC_TYPESHIFT (_IOC_NRSHIFT+_IOC_NRBITS) #define _IOC_TYPE(nr) (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK) #define _IOC_NR(nr) (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK) #endif int pwc_video_do_ioctl(struct pwc_softc *pdev, unsigned int cmd, void *arg, int unit) { #ifdef USB_DEBUG if (pwcdebug & TRACE_IOCTL) { switch (_IOC_TYPE(cmd)) { case 'v': printf(PWC_NAME": ioctl 0x%x (v4l1, VIDIOC%s)\n", cmd, (_IOC_NR(cmd) < V4L1_IOCTLS) ? v4l1_ioctls[_IOC_NR(cmd)] : "?"); break; default: printf(PWC_NAME": ioctl 0x%x (?)\n", cmd); } } #endif if (pdev->pwc_info.type == 0) { /* spwc camera */ int ret = spca5xx_ioctl_helper(&pdev->spca50x, cmd, arg); printf("spca5xx_ioctl_helper returns %d\n", ret); } switch (cmd) { case VIDIOCGCAP: /* Query cabapilities */ { struct video_capability *caps = arg; strncpy(caps->name, pdev->pwc_info.name, sizeof(caps->name) - 1); caps->name[sizeof(caps->name) - 1] = '\0'; caps->type = VID_TYPE_CAPTURE; caps->channels = 1; caps->audios = 1; caps->minwidth = pdev->view_min.x; caps->minheight = pdev->view_min.y; caps->maxwidth = pdev->view_max.x; caps->maxheight = pdev->view_max.y; break; } case VIDIOCGCHAN: /* Channel functions (simulate 1 channel) */ { struct video_channel *v = arg; if (v->channel != 0) return -EINVAL; v->flags = 0; v->tuners = 0; v->type = VIDEO_TYPE_CAMERA; strcpy(v->name, "Webcam"); return 0; } case VIDIOCSCHAN: /* set the channel */ { /* The spec says the argument is an integer, but the bttv driver uses a video_channel arg, which makes sense becasue it also has the norm flag. */ struct video_channel *v = arg; if (v->channel != 0) return -EINVAL; return 0; } case VIDIOCGPICT: /* Get Picture functions; contrast etc. */ { struct video_picture *p = arg; int val; val = pwc_get_brightness(pdev); p->brightness = (val >= 0) ? val : 0xffff; val = pwc_get_contrast(pdev); p->contrast = (val >= 0) ? (val << 10) : 0xffff; /* Gamma, Whiteness, what's the difference? :) */ val = pwc_get_gamma(pdev); p->whiteness = (val >= 0) ? (val << 11) : 0xffff; val = pwc_get_saturation(pdev); if (val >= 0) p->colour = 32768 + (val-256) * 327; else p->colour = 0xffff; p->depth = 24; p->palette = pdev->vpalette; p->hue = 0xFFFF; /* N/A */ break; } case VIDIOCSPICT: /* Set picture functions */ { struct video_picture *p = arg; /* * FIXME: Suppose we are mid read ANSWER: No problem: the firmware of the camera can handle brightness/contrast/etc changes at _any_ time, and the palette is used exactly once in the uncompress routine. */ pwc_set_brightness(pdev, p->brightness); pwc_set_contrast(pdev, p->contrast); pwc_set_gamma(pdev, p->whiteness); pwc_set_saturation(pdev, p->colour); if (p->palette && p->palette != pdev->vpalette) { switch (p->palette) { case VIDEO_PALETTE_YUV420P: case VIDEO_PALETTE_RAW: pdev->vpalette = p->palette; return pwc_try_video_mode(pdev, pdev->image.x, pdev->image.y, pdev->vframes, pdev->vcompression, pdev->vsnapshot); break; default: return -EINVAL; break; } } break; } case VIDIOCGWIN: /* Get Window/size parameters */ { struct video_window *vw = arg; vw->x = 0; vw->y = 0; vw->width = pdev->view.x; vw->height = pdev->view.y; vw->chromakey = 0; vw->flags = (pdev->vframes << PWC_FPS_SHIFT) | (pdev->vsnapshot ? PWC_FPS_SNAPSHOT : 0); break; } case VIDIOCSWIN: /* Set window/size */ { struct video_window *vw = arg; int fps, snapshot, ret; fps = (vw->flags & PWC_FPS_FRMASK) >> PWC_FPS_SHIFT; snapshot = vw->flags & PWC_FPS_SNAPSHOT; if (fps == 0) fps = pdev->vframes; if (pdev->view.x == vw->width && pdev->view.y == vw->height && fps == pdev->vframes && snapshot == pdev->vsnapshot) return 0; ret = pwc_try_video_mode(pdev, vw->width, vw->height, fps, pdev->vcompression, snapshot); if (ret) return ret; break; } case VIDIOCGFBUF: /* We don't have overlay support (yet) */ { struct video_buffer *vb = arg; memset(vb,0,sizeof(*vb)); break; } #ifdef USE_MMAP /* mmap() functions */ case VIDIOCGMBUF: { /* Tell the user program how much memory is needed for a mmap() */ struct video_mbuf *vm = arg; int i; memset(vm, 0, sizeof(*vm)); vm->size = pdev->pwc_mbufs * round_page(pdev->len_per_image); vm->frames = pdev->pwc_mbufs; /* double buffering should be enough for most applications */ for (i = 0; i < pdev->pwc_mbufs; i++) vm->offsets[i] = i * round_page(pdev->len_per_image); break; } case VIDIOCMCAPTURE: { /* Start capture into a given image buffer (called 'frame' in video_mmap structure) */ struct video_mmap *vm = arg; Trace(TRACE_READ, "VIDIOCMCAPTURE: %dx%d, frame %d, format %d\n", vm->width, vm->height, vm->frame, vm->format); if (vm->frame < 0 || vm->frame >= pdev->pwc_mbufs) return -EINVAL; /* xawtv is nasty. It probes the available palettes by setting a very small image size and trying various palettes... The driver doesn't support such small images, so I'm working around it. */ if (vm->format) { switch (vm->format) { case VIDEO_PALETTE_YUV420P: case VIDEO_PALETTE_RAW: break; default: return -EINVAL; break; } } if ((vm->width != pdev->view.x || vm->height != pdev->view.y) && (vm->width >= pdev->view_min.x && vm->height >= pdev->view_min.y)) { int ret; Trace(TRACE_OPEN, "VIDIOCMCAPTURE: changing size to please xawtv :-(.\n"); ret = pwc_try_video_mode(pdev, vm->width, vm->height, pdev->vframes, pdev->vcompression, pdev->vsnapshot); if (ret) return ret; } /* ... size mismatch */ /* FIXME: should we lock here? */ if (pdev->image_used[vm->frame]) return -EBUSY; /* buffer wasn't available. Bummer */ pdev->image_used[vm->frame] = 1; /* Okay, we're done here. In the SYNC call we wait until a frame comes available, then expand image into the given buffer. In contrast to the CPiA cam the Philips cams deliver a constant stream, almost like a grabber card. Also, we have separate buffers for the rawdata and the image, meaning we can nearly always expand into the requested buffer. */ Trace(TRACE_READ, "VIDIOCMCAPTURE done.\n"); break; } case VIDIOCSYNC: { /* The doc says: "Whenever a buffer is used it should call VIDIOCSYNC to free this frame up and continue." The only odd thing about this whole procedure is that MCAPTURE flags the buffer as "in use", and SYNC immediately unmarks it, while it isn't after SYNC that you know that the buffer actually got filled! So you better not start a CAPTURE in the same frame immediately (use double buffering). This is not a problem for this cam, since it has extra intermediate buffers, but a hardware grabber card will then overwrite the buffer you're working on. */ int *mbuf = arg; int ret; Trace(TRACE_READ, "VIDIOCSYNC called (%d).\n", *mbuf); /* bounds check */ if (*mbuf < 0 || *mbuf >= pdev->pwc_mbufs) return -EINVAL; /* check if this buffer was requested anyway */ if (pdev->image_used[*mbuf] == 0) return -EINVAL; pdev->fill_image = *mbuf; /* tell in which buffer we want the image to be expanded */ while ((ret = pwc_handle_frame(pdev)) == -EBUSY) { pdev->state |= PWC_ASLEEP; ret = tsleep(pdev, PZERO | PCATCH, "pwcsyn", 0); pdev->state &= ~PWC_ASLEEP; if(pdev->error_status) { pdev->image_used[*mbuf] = 0; return -pdev->error_status; } else if(ret) return -ret; } pdev->image_used[*mbuf] = 0; if(ret < 0) return ret; Trace(TRACE_READ, "VIDIOCSYNC: frame ready.\n"); break; } #endif case VIDIOCGAUDIO: { struct video_audio *v = arg; strcpy(v->name, "Microphone"); v->audio = -1; /* unknown audio minor */ v->flags = 0; v->mode = VIDEO_SOUND_MONO; v->volume = 0; v->bass = 0; v->treble = 0; v->balance = 0x8000; v->step = 1; break; } case VIDIOCSAUDIO: { /* Dummy: nothing can be set */ break; } case VIDIOCGUNIT: { struct video_unit *vu = arg; vu->video = unit; vu->audio = -1; /* not known yet */ vu->vbi = -1; vu->radio = -1; vu->teletext = -1; break; } default: return pwc_do_ioctl(pdev, cmd, arg); } /* ..switch */ return 0; } pwcbsd/pwcbsd/pwc-uncompress.h000644 000423 000000 00000002511 10546676137 017234 0ustar00luigiwheel000000 000000 /* * PWCBSD - Philips USB webcam driver for FreeBSD 5.4 and higher * * Copyright (C) 2006 Raaf * * Based on the Linux pwc driver. * * Copyright (C) 1999-2003 Nemosoft Unv. * Copyright (C) 2004-2006 Luc Saillard * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * 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 */ /* This file is the bridge between the kernel module and the plugin; it describes the structures and datatypes used in both modules. Any significant change should be reflected by increasing the pwc_decompressor_version major number. */ #ifndef PWC_UNCOMPRESS_H #define PWC_UNCOMPRESS_H #include "pwc-ioctl.h" /* from pwc-dec.h */ #define PWCX_FLAG_PLANAR 0x0001 /* */ #endif pwcbsd/pwcbsd/pwc.4000644 000423 000000 00000013524 10546676137 014762 0ustar00luigiwheel000000 000000 .Dd Februari 17, 2006 .Dt PWC 4 .Os .Sh NAME .Nm pwc .Nd USB webcam driver .Sh SYNOPSIS .Cd /dev/video0 .Sh DESCRIPTION The .Nm pwc driver provides support for various USB webcams. The driver is based on the Linux .Nm pwc webcam driver and uses the same API. .Pp The following device hints may be added to .Va /boot/device.hints to further configure the driver: .Pp .Bl -tag -width indent .It Va hint.pwc.0.power_save When power_save is enabled (set to 1), the module will try to shut down the cam on close() and re-activate on open(). This will save power and turn off the LED. Not all cameras support this though (the 645 and 646 don't have power saving at all), and some models don't work either (they will shut down, but never wake up). Consider this experimental. By default this option is disabled. .It Va hint.pwc.0.stats When enabled (set to 1) frame statistics are written to the console each time the device is closed. .It Va hint.pwc.0.led_on This setting defines the on time for the LED (in milliseconds). One of the interesting things that you can do with this is together with the .Va hint.pwc.0.led_off hint is to let the LED blink while the camera is in use. This: .Pp .Va hint.pwc.0.led_on="500" .br .Va hint.pwc.0.led_off="500" .Pp will blink the LED once every second. But with: .Pp .Va hint.pwc.0.led_on="0" .br .Va hint.pwc.0.led_off="0" .Pp the LED never goes on, making it suitable for silent surveillance. .Pp By default the camera's LED is on solid while in use, and turned off when the camera is not used anymore. .Pp This parameter works only with the ToUCam range of cameras (720, 730, 740, 750) and OEMs. For other cameras this command is silently ignored, and the LED cannot be controlled. .It Va hint.pwc.0.led_off This setting defines the off time for the LED (in milliseconds) .It Va hint.pwc.0.compression Sets the initial compression preference for all cameras. With this option you can control the compression factor that the camera uses to squeeze the image through the USB bus. You can set the parameter between 0 and 3: .Bl -tag -width indent .It 0 prefer uncompressed images; if the requested mode is not available in an uncompressed format, the driver will silently switch to low compression. .It 1 low compression. .It 2 medium compression. .It 3 high compression. .El .Pp High compression takes less bandwidth of course, but it could also introduce some unwanted artefacts. The default is 2, medium compression. .Pp The compression parameter does not apply to the 645 and 646 cameras and OEM models derived from those (only a few). Most cams honour this parameter. .It Va hint.pwc.0.fps Sets the default framerate when you open() the device; this is to accommodate some tools that don't set the framerate. Is an integer in the range of 5-30. .It Va hint.pwc.0.size Sets the default size when you open() the device; this is to accommodate some tools that don't set the size. Can be one of 'sqcif', 'qsif', 'qcif', 'sif', 'cif' or 'vga', for an image size of resp. 128x96, 160x120, 176x144, 320x240, 352x288 and 640x480 (of course, only for those cameras that support these resolutions). .It Va hint.pwc.0.pad Normally when an application requests an image size that is not supported by the webcam, the driver will try to set the webcam to a smaller supported image size and pad the resulting image with borders. If you set this parameter to 0 this behaviour is disabled and the driver will fail image size requests that are not supported by the webcam. .It Va hint.pwc.0.fbufs This parameter specifies the number of internal buffers to use for storing frames from the cam. This will help if the process that reads images from the cam is a bit slow or momentarily busy. However, on slow machines it only introduces lag, so choose carefully. The default is 3, which is reasonable. You can set it between 2 and 5 .El .Sh HARDWARE The .Nm driver supports the following USB webcams: .Pp .Bl -bullet -compact .It Philips PCA645VC .It Philips PCA646VC .It Philips PCVC675K (Vesta) .It Philips PCVC680K (Vesta Pro) .It Philips PCVC690K (Vesta Pro Scan) .It Philips PCVC730K (ToUCam Fun) .It Philips PCVC740K (ToUCam Pro) .It Philips PCVC830K (ToUCam Fun II) .It Philips PCVC840K (ToUCam Pro II) .It Philips PCVC750K (ToUCam Pro Scan) .It Philips PCVC720K/40 (ToUCam XS) .It Philips SPC900NC (ToUCam Pro III) .It Logitech QuickCam Pro 3000 .It Logitech QuickCam Notebook Pro (Old version) .It Logitech QuickCam Pro 4000 .It Logitech QuickCam Zoom .It Logitech QuickCam Zoom (new model) .It Logitech QuickCam Orbit/Sphere (Old version) .It Logitech QuickCam (reserved id 0x046D:0x08B6) .It Logitech QuickCam (reserved id 0x046D:0x08B7) .It Logitech QuickCam (reserved id 0x046D:0x08B8) .It Creative Labs Webcam 5 (Old version) .It Creative Labs Webcam Pro Ex .It Samsung MPC-C10 .It Samsung MPC-C30 .It Samsung SNC-35E .It Visionite VCS-UM100 .It Visionite VCS-UC300 .It Askey VC010 type 1 .It Askey VC010 type 2 .It AME Co. Afina Eye .It Sotec Afina Eye .El .Sh FILES .Bl -tag -width indent .It Sy /dev/video0 device node to access the driver .El .Sh EXAMPLES To watch the videostream of your webcam with mplayer: .br .Cd mplayer -demuxer rawvideo -rawvideo fps=15:w=320:h=240:i420 /dev/video0 .Pp To record the videostream of your webcam with mencoder: .br .Cd mencoder -demuxer rawvideo -rawvideo fps=15:w=320:h=240:i420 /dev/video0 -ovc lavc -lavcopts vcodec=mpeg4 -o webcam.avi .Pp .Cd Note: Both examples assume you have already set your webcam size to 'sif' and fps to 15. This can be done with pwcview(1): .Cd pwcview -h -s sif -f 15 .Sh SEE ALSO The driver's website: .br .D1 Pa http://raaf.atspace.org/ .Pp pwcview(1) .Sh COPYRIGHT This is free software licensed under the GPLv2; There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. .Pp Copyright (C) 2006 Raaf .Pp Based on the Linux .Nm pwc driver: .Pp Copyright (C) 1999-2003 Nemosoft Unv. .br Copyright (C) 2004-2006 Luc Saillard pwcbsd/pwcbsd/pwcview.1000644 000423 000000 00000011217 10546676137 015647 0ustar00luigiwheel000000 000000 .Dd Februari 17, 2006 .Dt PWCVIEW 1 .Os .Sh NAME .Nm pwcview .Nd view video, create jpeg snapshots and alter settings of a webcam controlled by the pwc(4) driver .Sh SYNOPSIS .Nm .Op options .Sh DESCRIPTION The .Nm utility is used to view the video, create jpeg snapshots and alter the settings of a webcam controlled by the pwc(4) driver. Since the webcam settings are retained by the pwc(4) driver after the device is closed you can also use this utility to set the size and framerate of your webcam before using it with another application that cannot do that by itself (Most applications can set the size but not the framerate by themselves) .Pp General options: .Pp .Bl -tag -width XXXXXXXXXXXX .It Fl ? Display help message .It Fl h Run .Nm in headless mode (Useful for automatic jpeg snapshots or for just changing the webcam's size and framerate) .El .Pp Device options: .Pp .Bl -tag -width XXXXXXXXXXXX .It Fl d Ar device Selects the video device to open. The default is /dev/video0 .It Fl s Ar size Sets the webcam's video size. The default is sif. Can be one of 'sqcif', 'qsif', 'qcif', 'sif', 'cif' or 'vga', for an image size of resp. 128x96, 160x120, 176x144,320x240, 352x288 and 640x480 (of course, only for those cameras that support these resolutions). .It Fl f Ar fps Sets the webcam's framerate. The default is 5. Should be in the range of 5-30. .El .Pp Video display options: .Pp .Bl -tag -width XXXXXXXXXXXX .It Fl x Create window without frame .It Fl y Use IYUV overlay instead of YV12 overlay .It Fl m Create the video surface in system memory (SDL_SWSURFACE) .It Fl a Always use video surface (SDL_ANYFORMAT) .It Fl b Ar bpp Bits per pixel to setup video surface with. The default is 0, which means to use the current display bits per pixel. .El .Pp JPEG output options: .Pp .Bl -tag -width XXXXXXXXXXXX .It Fl c Ar count Number of automatic jpeg snapshots to take. Set this to -1 for unlimited automatic jpeg snapshots. The default is 0 .It Fl i Ar interval Automatic jpeg snapshot interval in milliseconds. The default is 3000 .It Fl q Ar quality Quality of jpeg output images, range 0-100. The default is 75 .It Fl o Ar outfile Filename of the jpeg output images. This is a string that is passed to the strftime(3) function and thus can contain the format specifiers as documented in the strftime(3) manpage. The default is /tmp/%Y%m%d%H%M%S.jpg .It Fl e Ar command Command to execute after each jpeg snapshot. This command will be executed with the filename of the jpeg image as it's only command line argument. If you want to execute a command that requires multiple arguments, you can create a wrapper shell script. The default is not to execute any command. .El .Pp With .Nm you can alter various settings of your webcam. It provides a menu like structure where you can specify what setting of your webcam you want to alter with the .Va up arrow and .Va down arrow. The current menu item and thus the setting to alter will then be displayed in the title bar. .Pp Most settings can be altered by the .Va left arrow and .Va right arrow. In the .Va gain control, shutter speed, whitebalance and .Va contour menus you can also use the .Va enter key to switch between .Va auto and .Va manual modes. In addition the .Va enter key is also used in the .Va dnr mode, backlight compensation, anti flicker, colour mode, save user settings, restore user settings and .Va restore factory settings menus. .Pp Finally you can use the .Va f key to toggle full screen mode, the .Va p key to take a manual jpeg snapshot, the .Va space key to freeze and unfreeze the picture and the .Va q key to quit the application .Sh VIDEODISPLAY .Nm uses the SDL library to display the video of your webcam. The default video mode used by .Nm is in general the most efficient, but this is very much dependent on the capabilities of your video card. If it doesn't quite work the way you want it to, you can tune the video settings with any of the command line options listed in the "Video display options" section at the beginning of this manpage. See the SDL_SetVideoMode(3) manpage for exact details. .Sh EXAMPLES Set the webcam to vga mode at 15 fps and execute the firefox command whenever a jpeg snapshot is taken: .Pp .Dl "pwcview -s vga -f 15 -e firefox" .Pp Take one automatic jpeg snapshot image using the default sif resolution, save it as foo.jpg and then exit: .Pp .Dl "pwcview -h -c 1 -o foo.jpg" .Pp .Sh SEE ALSO .Pp The Linux PWC Documentation Project: .Pp .D1 Pa http://www.lavrsen.dk/twiki/bin/view/PWC/WebHome .Pp pwc(4), SDL_SetVideoMode(3), strftime(3) .Sh COPYRIGHT This is free software licensed under the GPLv2; There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. .Pp Copyright (C) 2006 Raaf pwcbsd/pwcbsd/pwcview.c000644 000423 000000 00000054656 10551677522 015742 0ustar00luigiwheel000000 000000 /* * pwcview - application to view video, create jpeg snapshots and alter * settings of a webcam controlled by the pwc driver * * Copyright (C) 2006 Raaf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * 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 #include #include #include #include #include #include #include #include #include #include #include #ifndef NOGUI #include #endif #ifdef LOCAL_VIDEODEV #include "videodev.h" #else #include #endif #include "pwc-ioctl.h" #ifndef NOGUI int default_handler(int fd, int dir, char *buf) { if(dir != 0) return -1; snprintf(buf,80,"pwcview"); return 0; } int framerate_handler(int fd, int dir, char *buf) { int fps; struct video_window vw; if(ioctl(fd,VIDIOCGWIN,&vw) == -1) { perror("Failed to get current framerate"); return -1; } fps = vw.flags >> PWC_FPS_SHIFT; if((dir == -1 && fps >= 9) ||(dir == 1 && fps <= 25)) { fps += dir == -1 ? -5 : 5; vw.flags = fps << PWC_FPS_SHIFT; if(ioctl(fd,VIDIOCSWIN,&vw) == -1) fprintf(stderr,"Failed to set framerate to %d fps: %s\n",fps,strerror(errno)); if(ioctl(fd,VIDIOCGWIN,&vw) == -1) { perror("Failed to get new framerate"); return -1; } fps = vw.flags >> PWC_FPS_SHIFT; } snprintf(buf,80,"framerate: %d fps",fps); return 0; } int compression_handler(int fd, int dir, char *buf) { int qual; if(ioctl(fd,VIDIOCPWCGCQUAL,&qual) == -1) { perror("Failed to get current compression"); return -1; } if((dir == -1 && qual > 0) || (dir == 1 && qual < 3)) { qual += dir == -1 ? -1 : 1; if(ioctl(fd,VIDIOCPWCSCQUAL,&qual) == -1) perror("Failed to set compression"); if(ioctl(fd,VIDIOCPWCGCQUAL,&qual) == -1) { perror("Failed to get new compression"); return -1; } } snprintf(buf,80,"compression: %d",qual); return 0; } int brightness_handler(int fd, int dir, char *buf) { struct video_picture pict; if(ioctl(fd,VIDIOCGPICT,&pict) == -1) { perror("Failed to get current brightness"); return -1; } if((dir == -1) || (dir == 1)) { pict.brightness += (dir == -1) ? -512 : 512; if(ioctl(fd,VIDIOCSPICT,&pict) == -1) perror("Failed to set brightness"); if(ioctl(fd,VIDIOCGPICT,&pict) == -1) { perror("Failed to get new brightness"); return -1; } } snprintf(buf,80,"brightness: %d",pict.brightness >> 9); return 0; } int contrast_handler(int fd, int dir, char *buf) { struct video_picture pict; if(ioctl(fd,VIDIOCGPICT,&pict) == -1) { perror("Failed to get current contrast"); return -1; } if((dir == -1) || (dir == 1)) { pict.contrast += dir == -1 ? -1024 : 1024; if(ioctl(fd,VIDIOCSPICT,&pict) == -1) perror("Failed to set contrast"); if(ioctl(fd,VIDIOCGPICT,&pict) == -1) { perror("Failed to get new contrast"); return -1; } } snprintf(buf,80,"contrast: %d",pict.contrast >> 10); return 0; } int saturation_handler(int fd, int dir, char *buf) { struct video_picture pict; if(ioctl(fd,VIDIOCGPICT,&pict) == -1) { perror("Failed to get current saturation"); return -1; } if((dir == -1) || (dir == 1)) { pict.colour += dir == -1 ? -327 : 327; if(ioctl(fd,VIDIOCSPICT,&pict) == -1) perror("Failed to set saturation"); if(ioctl(fd,VIDIOCGPICT,&pict) == -1) { perror("Failed to get new saturation"); return -1; } } snprintf(buf,80,"saturation: %d",(pict.colour - 32768) / 327); return 0; } int gamma_handler(int fd, int dir, char *buf) { struct video_picture pict; if(ioctl(fd,VIDIOCGPICT,&pict) == -1) { perror("Failed to get current gamma"); return -1; } if((dir == -1) ||(dir == 1)) { pict.whiteness += dir == -1 ? -2048 : 2048; if(ioctl(fd,VIDIOCSPICT,&pict) == -1) perror("Failed to set gamma"); if(ioctl(fd,VIDIOCGPICT,&pict) == -1) { perror("Failed to get new gamma"); return -1; } } snprintf(buf,80,"gamma: %d",pict.whiteness >> 11); return 0; } int agc_handler(int fd, int dir, char *buf) { static u_int16_t agc = 32768; static int agcmode = 1; int val; if(dir == 2) { if(++agcmode == 2) agcmode = 0; } else if(dir == -1 && agcmode == 0) agc -= 1024; else if(dir == 1 && agcmode == 0) agc += 1024; if(agcmode == 1) { val = -1; snprintf(buf,80,"gain control: auto"); } else { val = agc; snprintf(buf,80,"gain control: %d",agc >> 10); } ioctl(fd,VIDIOCPWCSAGC,&val); return 0; } int shutter_handler(int fd, int dir, char *buf) { static u_int16_t shutter = 32768; static int shuttermode = 1; int val; if(dir == 2) { if(++shuttermode == 2) shuttermode = 0; } else if(dir == -1 && shuttermode == 0) shutter -= 256; else if(dir == 1 && shuttermode == 0) shutter += 256; if(shuttermode == 1) { val = -1; snprintf(buf,80,"shutter speed: auto"); } else { val = shutter; snprintf(buf,80,"shutter speed: %d",shutter >> 8); } ioctl(fd,VIDIOCPWCSSHUTTER,&val); return 0; } int whitebalance_handler(int fd, int dir, char *buf) { static int skip = 0; struct pwc_whitebalance wb; char *names[] = { "indoor", "outdoor", "fluorescent","manual","auto" }; int *val = NULL; if(ioctl(fd,VIDIOCPWCGAWB,&wb) == -1) { perror("Failed to get whitebalance"); return -1; } if(dir == 2 && !skip) { if(--wb.mode < PWC_WB_INDOOR) wb.mode = PWC_WB_AUTO; } if(wb.mode == PWC_WB_MANUAL) { if(dir == 2) { skip = !skip; if(skip) { wb.manual_red = wb.read_red; wb.manual_blue = wb.read_blue; } } val = skip ? &wb.manual_red : &wb.manual_blue; if(dir == -1) *val -= 256; else if(dir == 1) *val += 256; } if(ioctl(fd,VIDIOCPWCSAWB,&wb) == -1) perror("Failed to set whitebalance"); if(ioctl(fd,VIDIOCPWCGAWB,&wb) == -1) { perror("Failed to get whitebalance"); return -1; } if(wb.mode == PWC_WB_MANUAL) snprintf(buf,80,"whitebalance %s gain: %d",skip ? "red" : "blue",*val >> 8); else snprintf(buf,80,"whitebalance: %s",names[wb.mode]); return 0; } int whitebalancespeed_handler(int fd, int dir, char *buf) { struct pwc_wb_speed speed; if(ioctl(fd,VIDIOCPWCGAWBSPEED,&speed) == -1) { perror("Failed to get current awb speed"); return -1; } if((dir == -1) || (dir == 1)) { speed.control_speed += dir == -1 ? -2032 : 2032; if(ioctl(fd,VIDIOCPWCSAWBSPEED,&speed) == -1) perror("Failed to set awb speed"); if(ioctl(fd,VIDIOCPWCGAWBSPEED,&speed) == -1) { perror("Failed to get new awb speed"); return -1; } } snprintf(buf,80,"whitebalance speed: %d",speed.control_speed / 2032); return 0; } int whitebalancedelay_handler(int fd, int dir, char *buf) { struct pwc_wb_speed speed; if(ioctl(fd,VIDIOCPWCGAWBSPEED,&speed) == -1) { perror("Failed to get current awb delay"); return -1; } if((dir == -1) || (dir == 1)) { speed.control_delay += dir == -1 ? -1024 : 1024; if(ioctl(fd,VIDIOCPWCSAWBSPEED,&speed) == -1) perror("Failed to set awb delay"); if(ioctl(fd,VIDIOCPWCGAWBSPEED,&speed) == -1) { perror("Failed to get new awb delay"); return -1; } } snprintf(buf,80,"whitebalance delay: %d",speed.control_delay >> 10); return 0; } int contour_handler(int fd, int dir,char *buf) { static u_int16_t contour = 32768; static int contourmode = 1; int val; if(dir == 2) { if(++contourmode == 2) contourmode = 0; } else if(dir == -1 && contourmode == 0) contour -= 1024; else if(dir == 1 && contourmode == 0) contour += 1024; if(contourmode == 1) val = -1; else val = contour; if(ioctl(fd,VIDIOCPWCSCONTOUR,&val) == -1) perror("Failed to set contour"); if(contourmode == 1) snprintf(buf,80,"contour: auto"); else { if(ioctl(fd,VIDIOCPWCGCONTOUR,&contour) == -1) { perror("Failed to get contour"); return -1; } snprintf(buf,80,"contour: %d",contour >> 10); } return 0; } int dynamicnoise_handler(int fd, int dir, char *buf) { int dynnoise; if(ioctl(fd,VIDIOCPWCGDYNNOISE,&dynnoise) == -1) { perror("Failed to get current dynamic noise reduction mode"); return -1; } if(dir == 2) { if(++dynnoise == 4) dynnoise = 0; if(ioctl(fd,VIDIOCPWCSDYNNOISE,&dynnoise) == -1) perror("Failed to set dynamic noise reduction mode"); if(ioctl(fd,VIDIOCPWCGDYNNOISE,&dynnoise) == -1) { perror("Failed to get new dynamic noise reduction mode"); return -1; } } snprintf(buf,80,"dnr mode: %d",dynnoise); return 0; } int backlight_handler(int fd, int dir, char *buf) { int backlight; if(ioctl(fd,VIDIOCPWCGBACKLIGHT,&backlight) == -1) { perror("Failed to get backlight mode"); return -1; } if(dir == 2) { backlight = !backlight; if(ioctl(fd,VIDIOCPWCSBACKLIGHT,&backlight) == -1) perror("Failed to set backlight mode"); if(ioctl(fd,VIDIOCPWCGBACKLIGHT,&backlight) == -1) { perror("Failed to get new backlight mode"); return -1; } } snprintf(buf,80,"backlight compensation: %s",backlight ? "on" : "off"); return 0; } int flicker_handler(int fd, int dir, char *buf) { int flicker; if(ioctl(fd,VIDIOCPWCGFLICKER,&flicker) == -1) { perror("Failed to get flicker mode"); return -1; } if(dir == 2) { flicker = !flicker; if(ioctl(fd,VIDIOCPWCSFLICKER,&flicker) == -1) perror("Failed to set flicker mode"); if(ioctl(fd,VIDIOCPWCGFLICKER,&flicker) == -1) { perror("Failed to get new flicker mode"); return -1; } } snprintf(buf,80,"anti flicker mode: %s",flicker ? "on" : "off"); return 0; } int colour_handler(int fd, int dir, char *buf) { int colour; if(ioctl(fd,VIDIOCPWCGCOLOUR,&colour) == -1) { perror("Failed to get colour mode"); return -1; } if(dir == 2) { colour = !colour; if(ioctl(fd,VIDIOCPWCSCOLOUR,&colour) == -1) perror("Failed to set colour mode"); if(ioctl(fd,VIDIOCPWCGCOLOUR,&colour) == -1) { perror("Failed to get new colour mode"); return -1; } } snprintf(buf,80,"colour mode: %s",colour ? "color" : "black & white"); return 0; } int saveuser_handler(int fd, int dir, char *buf) { if(dir == 0) { snprintf(buf,80,"save user settings"); } else if(dir == 2) { if(ioctl(fd,VIDIOCPWCSUSER) == -1) snprintf(buf,80,"Error: %s",strerror(errno)); else snprintf(buf,80,"User settings saved"); return 0; } else { return -1; } return 0; } int restoreuser_handler(int fd, int dir, char *buf) { if(dir == 0) { snprintf(buf,80,"restore user settings"); } else if(dir == 2) { if(ioctl(fd,VIDIOCPWCRUSER) == -1) snprintf(buf,80,"Error: %s",strerror(errno)); else snprintf(buf,80,"User settings restored"); } else { return -1; } return 0; } int restorefactory_handler(int fd, int dir, char *buf) { if(dir == 0) { snprintf(buf,80,"restore factory settings"); } else if(dir == 2) { if(ioctl(fd,VIDIOCPWCFACTORY) == -1) snprintf(buf,80,"Error: %s",strerror(errno)); else snprintf(buf,80,"Factory settings restored"); } else { return -1; } return 0; } int (*handler[])(int fd, int direction, char *buf) = { /* direction: -1 = down, 0 = init, 1 = up, 2 = special */ default_handler, framerate_handler, brightness_handler, contrast_handler, saturation_handler, gamma_handler, agc_handler, shutter_handler, whitebalance_handler, whitebalancespeed_handler, whitebalancedelay_handler, contour_handler, dynamicnoise_handler, backlight_handler, flicker_handler, colour_handler, compression_handler, saveuser_handler, restoreuser_handler, restorefactory_handler }; Uint32 cbtimer(Uint32 interval, void *param) { SDL_Event event; SDL_UserEvent userevent; userevent.type = SDL_USEREVENT; userevent.code = 0; userevent.data1 = NULL; userevent.data2 = NULL; event.type = SDL_USEREVENT; event.user = userevent; SDL_PushEvent(&event); return interval; } #endif void sig_chld(int signo) { int stat; while(waitpid(-1, &stat, WNOHANG) > 0) ; } void jpeg_init(int width, int height, int quality, struct jpeg_compress_struct *cinfo, struct jpeg_error_mgr *jerr, JSAMPIMAGE jimage, JSAMPROW y) { int i; JSAMPROW u,v; cinfo->err = jpeg_std_error(jerr); jpeg_create_compress(cinfo); cinfo->image_width = width; cinfo->image_height = height; cinfo->input_components = 3; cinfo->in_color_space = JCS_YCbCr; jpeg_set_defaults(cinfo); /* cinfo->dct_method = JDCT_FLOAT; */ cinfo->raw_data_in = TRUE; cinfo->comp_info[0].h_samp_factor = 2; cinfo->comp_info[0].v_samp_factor = 2; cinfo->comp_info[1].h_samp_factor = 1; cinfo->comp_info[1].v_samp_factor = 1; cinfo->comp_info[2].h_samp_factor = 1; cinfo->comp_info[2].v_samp_factor = 1; jimage[0] = malloc(height * 2 * sizeof(JSAMPROW)); if(jimage[0] == NULL) { fprintf(stderr,"Error: out of memory\n"); exit(1); } jimage[1] = jimage[0] + height; jimage[2] = jimage[1] + (height/2); u = y + width * height; v = u + width * height / 4; for(i = 0; i < height; ++i, y+=width) { jimage[0][i] = y; } for(i = 0; i < height/2; ++i, u+=width/2, v+=width/2) { jimage[1][i] = u; jimage[2][i] = v; } jpeg_set_quality(cinfo, quality, TRUE); } void jpeg_write(int height, JSAMPIMAGE jimage, struct jpeg_compress_struct *cinfo, const char *fmt, const char *cmd) { JSAMPARRAY jdata[3]; char filename[1024]; FILE *outfile; time_t tt; struct tm *tm; int i; tt = time(NULL); if(tt == (time_t)-1) { perror("Failed to get time"); return; } tm = localtime(&tt); if(strftime(filename,1024,fmt,tm) == 0) { fprintf(stderr,"Error: resulting filename to long\n"); return; } if ((outfile = fopen(filename, "wb")) == NULL) { perror("Error opening output file"); return; } jdata[0] = jimage[0]; jdata[1] = jimage[1]; jdata[2] = jimage[2]; jpeg_stdio_dest(cinfo, outfile); jpeg_start_compress(cinfo, TRUE); for (i = 0;i < height;i += 2*DCTSIZE) { jpeg_write_raw_data(cinfo, jdata, 2*DCTSIZE); jdata[0] += 2*DCTSIZE; jdata[1] += DCTSIZE; jdata[2] += DCTSIZE; } jpeg_finish_compress(cinfo); fclose(outfile); if(cmd != NULL) { switch(fork()) { case 0: execlp(cmd,cmd,filename,NULL); fprintf(stderr,"Failed to execute %s: %s\n",cmd,strerror(errno)); _exit(1); case -1: perror("fork failed"); /* Fall through */ default: break; } } } #define PSZ_MAX 6 struct { char *name; int width; int height; } sizes[PSZ_MAX] = { { "sqcif", 128, 96 }, { "qsif", 160, 120 }, { "qcif", 176, 144 }, { "sif", 320, 240 }, { "cif", 352, 288 }, { "vga", 640, 480 } }; int usage() { fprintf(stderr, #ifndef NOGUI "Usage: pwcview [ options ]\n\n" #else "Usage: pwcsnap [ options ]\n\n" #endif "Options:\n" " -? Display this help message\n" #ifndef NOGUI " -h Run in headless mode\n" " -x Create window without frame\n" " -y Use IYUV overlay instead of YV12 overlay\n" " -m Create the video surface in system memory (SDL_SWSURFACE)\n" " -a Always use video surface (SDL_ANYFORMAT)\n" " -b Bits per pixel to setup SDL video surface with (default: 0)\n" #endif " -c Number of jpeg snapshots to take (default: 0 (-1=unlimited))\n" " -i Jpeg snapshot interval in milliseconds (default: 3000)\n" " -q Quality of jpeg output image (range: 0 -100, default: 75)\n" " -o Filename for jpeg output (default: /tmp/%%Y%%m%%d%%H%%M%%S.jpg)\n" " -e Command to execute after each snaphot (default: none)\n" " -d Video device to open (default: /dev/video0)\n" " -s Video size to use (default: sif)\n" " -f Video framerate to use (default: 5)\n\n" " See the pwcview(1) manpage for details\n\n"); return 1; } int main(int argc, char **argv) { #ifndef NOGUI Uint8 *keylist; SDL_Surface *screen; SDL_Overlay *overlay; SDL_Event event; SDL_TimerID timerid; SDL_Rect rect = { 0 }; int initflags = SDL_INIT_VIDEO; int sdlflags = SDL_RESIZABLE; int format = SDL_YV12_OVERLAY; int swsurface = 0; int bpp = 0; int ysize, uvsize, rv; unsigned char *u, *v; int fullscreen = 0, mode = 0, failed = 0; char buf[80]; Uint32 interval = 3000; int headless = 0; #else unsigned int interval = 3000; int headless = 1; #endif struct timespec tv; struct video_window vw = { 0 }; const char *device = "/dev/video0"; unsigned int fps = 5; int snapcnt = 0; int frozen = 0; int i = 3; /* sizeidx (sif) */ JSAMPARRAY jdata[3]; struct jpeg_compress_struct cinfo; struct jpeg_error_mgr jerr; int quality = 75; const char *outfile = "/tmp/%Y%m%d%H%M%S.jpg"; const char *command = NULL; int fd; int imgsize; int ch, size; unsigned char *y; while((ch = getopt(argc,argv,"yaxhmb:q:o:d:e:f:i:c:s:?")) != -1) { switch(ch) { #ifndef NOGUI case 'y': format = SDL_IYUV_OVERLAY; break; case 'a': sdlflags |= SDL_ANYFORMAT; break; case 'x': sdlflags |= SDL_NOFRAME; break; case 'h': headless = 1; break; case 'm': swsurface = 1; break; case 'b': bpp = atoi(optarg); break; #endif case 'q': quality = atoi(optarg); break; case 'o': outfile = optarg; break; case 'd': device = optarg; break; case 'e': command = optarg; break; case 'f': fps = strtoul(optarg,NULL,10); break; case 'i': interval = strtoul(optarg,NULL,10); break; case 'c': snapcnt = atoi(optarg); if(snapcnt < -1) { fprintf(stderr,"Invalid snapshot count: %d\n",snapcnt); return 1; } break; case 's': for(i = 0; i < PSZ_MAX; ++i) if(strcmp(sizes[i].name,optarg) == 0) break; if(i == PSZ_MAX) { fprintf(stderr,"Invalid size, valid sizes: sqcif, qsif, qcif, sif, cif, vga\n"); return 1; } break; case '?': default: return usage(); } } if(fps < 5 || fps > 30) { fprintf(stderr,"Invalid framerate, framerate must be in the range 5-30\n"); return 1; } if(!headless && interval < (((1000 / fps)/10)*10)) interval = (((1000 / fps)/10)*10); vw.width = sizes[i].width; vw.height= sizes[i].height; vw.flags = fps << PWC_FPS_SHIFT; imgsize = (vw.width * vw.height * 3)/2; if((fd = open(device, O_RDONLY)) < 0) { perror("Failed to open webcam"); exit(1); } fcntl(fd,F_SETFD,FD_CLOEXEC); if(ioctl(fd,VIDIOCSWIN,&vw) == -1) { fprintf(stderr,"Failed to set webcam to: %dx%d (%s) at %d fps (%s)\n", vw.width,vw.height,sizes[i].name,fps,strerror(errno)); exit(1); } fprintf(stderr,"Webcam set to: %dx%d (%s) at %d fps\n",vw.width,vw.height,sizes[i].name,fps); if(headless && snapcnt == 0) { /* Done */ close(fd); exit(0); } y = malloc(imgsize); if(y == NULL) { perror("Out of memory"); exit(1); } jpeg_init(vw.width,vw.height,quality,&cinfo,&jerr,jdata,(JSAMPROW)y); if(command != NULL) signal(SIGCHLD,sig_chld); #ifndef NOGUI if(!headless) { rect.w = vw.width; rect.h = vw.height; ysize = rect.w * rect.h; uvsize = ysize / 4; if(format == SDL_IYUV_OVERLAY) { u = y + ysize; v = u + uvsize; } else { v = y + ysize; u = v + uvsize; } if(snapcnt != 0) initflags |= SDL_INIT_TIMER; if (SDL_Init(initflags) < 0) { fprintf(stderr,"Failed to init sdl: %s\n", SDL_GetError()); exit(1); } atexit(SDL_Quit); sdlflags |= swsurface ? SDL_SWSURFACE : SDL_HWSURFACE; if((screen = SDL_SetVideoMode(rect.w, rect.h, bpp, sdlflags)) == NULL) { fprintf(stderr,"SDL Failed to set videomode: %s\n", SDL_GetError()); exit(1); } if((overlay = SDL_CreateYUVOverlay(rect.w, rect.h, format, screen)) == NULL) { fprintf(stderr,"Failed to create yuvoverlay: %s\n", SDL_GetError()); exit(1); } SDL_DisplayYUVOverlay(overlay, &rect); snprintf(buf,80,"pwcview"); keylist = SDL_GetKeyState(NULL); if(snapcnt != 0) timerid = SDL_AddTimer(interval,cbtimer,NULL); } #endif while (frozen || ((size = read(fd,y,imgsize)) > 0) || (size == -1 && errno == EINTR)) { if(!frozen && size != imgsize) { if(size != -1) { fprintf(stderr,"Warning short read, got only %d of %d bytes\n",size,imgsize); } continue; } if(headless) { jpeg_write(vw.height,jdata,&cinfo,outfile,command); if(snapcnt > 0) snapcnt--; if(snapcnt == 0) exit(0); tv.tv_sec = interval / 1000; tv.tv_nsec = (interval % 1000) * 1000000; while(nanosleep(&tv,&tv) == -1 && errno == EINTR) ; continue; } #ifndef NOGUI SDL_LockYUVOverlay(overlay); memcpy(overlay->pixels[0],y,ysize); memcpy(overlay->pixels[1],u,uvsize); memcpy(overlay->pixels[2],v,uvsize); SDL_UnlockYUVOverlay(overlay); SDL_DisplayYUVOverlay(overlay, &rect); SDL_PumpEvents(); if(failed == 0) SDL_WM_SetCaption(buf,buf); failed = 1; if(keylist[SDLK_RIGHT]) { failed = handler[mode](fd,1,buf); continue; } else if(keylist[SDLK_LEFT]) { failed = handler[mode](fd,-1,buf); continue; } if(frozen) rv = SDL_WaitEvent(&event); else rv= SDL_PollEvent(&event); if(rv) { do { if(event.type == SDL_KEYDOWN) { switch(event.key.keysym.sym) { case SDLK_DOWN: if(mode != sizeof(handler)/sizeof(handler[0]) - 1) failed = handler[++mode](fd,0,buf); break; case SDLK_UP: if(mode != 0) failed = handler[--mode](fd,0,buf); break; case SDLK_RETURN: failed = handler[mode](fd,2,buf); break; case SDLK_f: SDL_WM_ToggleFullScreen(screen); fullscreen = !fullscreen; SDL_WM_GrabInput(fullscreen ? SDL_GRAB_ON : SDL_GRAB_OFF); break; case SDLK_p: jpeg_write(vw.height,jdata,&cinfo,outfile,command); break; case SDLK_SPACE: frozen = !frozen; break; case SDLK_q: exit(0); default: break; } } else if(event.type == SDL_VIDEORESIZE) { rect.w = event.resize.w; rect.h = event.resize.h; if((screen = SDL_SetVideoMode(rect.w, rect.h, bpp, sdlflags)) == NULL) { fprintf(stderr,"SDL Failed to set videomode: %s\n", SDL_GetError()); exit(1); } } else if(event.type == SDL_USEREVENT) { jpeg_write(vw.height,jdata,&cinfo,outfile,command); if(snapcnt > 0) snapcnt--; if(snapcnt == 0) SDL_RemoveTimer(timerid); } else if(event.type == SDL_QUIT) { exit(0); } }while(!frozen && SDL_PollEvent(&event)); } #endif } if(size != 0) perror("Error reading from webcam"); close(fd); jpeg_destroy_compress(&cinfo); return 0; } pwcbsd/pwcbsd/spca_globals.h000644 000423 000000 00000005741 10553461760 016700 0ustar00luigiwheel000000 000000 /* * spca_globals.h * * $FreeBSD$ */ #ifndef _SPCA_GLOBALS_H #define _SPCA_GLOBALS_H struct usb_spca50x; extern int pwcdebug; int spca50x_init_source(struct usb_spca50x *spca50x); int spca50x_configure(struct usb_spca50x *spca50x); int spca50x_set_packet_size(struct usb_spca50x *spca50x, int size); void spcaCameraStart(struct usb_spca50x *spca50x); void spca50x_stop_isoc(struct usb_spca50x *spca50x); int spca5xx_setMode(struct usb_spca50x *spca50x, int width, int height, int format); int _unused_spca50x_move_data_helper(struct usb_spca50x *spca50x, unsigned char *cdata, int datalength); int spca5xx_ioctl_helper(struct usb_spca50x *spca50x, unsigned int cmd, void *arg); int spcaDetectCamera(int vendor, int product, struct pwc_softc *sc); __u16 spca50x_get_brightness(struct usb_spca50x *spca50x); void spca50x_set_brightness(struct usb_spca50x *spca50x, __u8 brightness); void init_jpeg_decoder(struct usb_spca50x *spca50x); struct usb_quickcam; int usb_quickcam_stop(struct usb_quickcam *quickcam); void quickcam_parse_data(struct usb_quickcam *quickcam, int curframe); int usb_quickcam_upload_frame(struct usb_quickcam *quickcam); int config_quickcam(struct usb_spca50x *spca50x); int spca50x_outpicture(struct spca50x_frame *myframe); /* from qce-ga/ */ /* * Structure filled in for each of the types of sensor (HDCS, PB0100) */ struct sensorctrl { int (*init) (struct usb_device * dev, int mode, int *rgain, int *bgain, int *ggain, struct sensorctrl *sensor_ctrl); int (*set_shutter) (struct usb_device * dev, int sval, int xval, struct sensorctrl *sensor_ctrl); int (*set_gains) (struct usb_device * dev, int rgain, int bgain, int ggain); int (*set_window) (struct usb_device * dev, int x, int y, int w, int h, struct sensorctrl *sensor_ctrl); int (*set_size) (struct usb_device * dev, int mode); int (*start) (struct usb_device * dev, struct sensorctrl *sensor_ctrl); int (*stop) (struct usb_device * dev, struct sensorctrl *sensor_ctrl); int width, height; // size delivered by the sensor. int mode; // mode: 0 full; 1 half; 2 quater. /* addresses used for the hdcs sensor */ unsigned char control; unsigned char config; }; int usb_quickcam_set1(struct usb_device *dev, short reg, char val); int usb_quickcam_get_i2c(struct usb_device *dev, unsigned char sensor_addr, int reg, void *buf, int len); int qc_sensor_init(struct usb_spca50x *spca50x); /* em28xx setup */ int em28xx_read_reg_req(struct usb_device *dev, uint16_t req, uint16_t reg); int em28xx_read_reg(struct usb_device *dev, uint16_t reg); int em28xx_write_regs_req(struct usb_device *dev, uint16_t req, uint16_t reg, uint8_t *buf, uint16_t len); int em28xx_write_regs(struct usb_device *dev, uint16_t reg, uint8_t *buf, uint16_t len); struct sensorctrl; struct sensor_data { int name; int reg23; unsigned char i2c_addr; int id_reg; unsigned char id; int length_id; void (*load)(struct sensorctrl *); }; #endif /* _SPCA_GLOBALS_H */ pwcbsd/pwcbsd/videodev.h000644 000423 000000 00000027103 10546676140 016052 0ustar00luigiwheel000000 000000 #ifndef __LINUX_VIDEODEV_H #define __LINUX_VIDEODEV_H #include typedef int32_t __s32; typedef uint32_t __u32; typedef uint16_t __u16; typedef uint8_t __u8; #if 0 #define HAVE_V4L2 1 #include #endif #define VID_TYPE_CAPTURE 1 /* Can capture */ #define VID_TYPE_TUNER 2 /* Can tune */ #define VID_TYPE_TELETEXT 4 /* Does teletext */ #define VID_TYPE_OVERLAY 8 /* Overlay onto frame buffer */ #define VID_TYPE_CHROMAKEY 16 /* Overlay by chromakey */ #define VID_TYPE_CLIPPING 32 /* Can clip */ #define VID_TYPE_FRAMERAM 64 /* Uses the frame buffer memory */ #define VID_TYPE_SCALES 128 /* Scalable */ #define VID_TYPE_MONOCHROME 256 /* Monochrome only */ #define VID_TYPE_SUBCAPTURE 512 /* Can capture subareas of the image */ #define VID_TYPE_MPEG_DECODER 1024 /* Can decode MPEG streams */ #define VID_TYPE_MPEG_ENCODER 2048 /* Can encode MPEG streams */ #define VID_TYPE_MJPEG_DECODER 4096 /* Can decode MJPEG streams */ #define VID_TYPE_MJPEG_ENCODER 8192 /* Can encode MJPEG streams */ struct video_capability { char name[32]; int type; int channels; /* Num channels */ int audios; /* Num audio devices */ int maxwidth; /* Supported width */ int maxheight; /* And height */ int minwidth; /* Supported width */ int minheight; /* And height */ }; struct video_channel { int channel; char name[32]; int tuners; __u32 flags; #define VIDEO_VC_TUNER 1 /* Channel has a tuner */ #define VIDEO_VC_AUDIO 2 /* Channel has audio */ __u16 type; #define VIDEO_TYPE_TV 1 #define VIDEO_TYPE_CAMERA 2 __u16 norm; /* Norm set by channel */ }; struct video_tuner { int tuner; char name[32]; unsigned long rangelow, rangehigh; /* Tuner range */ __u32 flags; #define VIDEO_TUNER_PAL 1 #define VIDEO_TUNER_NTSC 2 #define VIDEO_TUNER_SECAM 4 #define VIDEO_TUNER_LOW 8 /* Uses KHz not MHz */ #define VIDEO_TUNER_NORM 16 /* Tuner can set norm */ #define VIDEO_TUNER_STEREO_ON 128 /* Tuner is seeing stereo */ #define VIDEO_TUNER_RDS_ON 256 /* Tuner is seeing an RDS datastream */ #define VIDEO_TUNER_MBS_ON 512 /* Tuner is seeing an MBS datastream */ __u16 mode; /* PAL/NTSC/SECAM/OTHER */ #define VIDEO_MODE_PAL 0 #define VIDEO_MODE_NTSC 1 #define VIDEO_MODE_SECAM 2 #define VIDEO_MODE_AUTO 3 __u16 signal; /* Signal strength 16bit scale */ }; struct video_picture { __u16 brightness; __u16 hue; __u16 colour; __u16 contrast; __u16 whiteness; /* Black and white only */ __u16 depth; /* Capture depth */ __u16 palette; /* Palette in use */ #define VIDEO_PALETTE_GREY 1 /* Linear greyscale */ #define VIDEO_PALETTE_HI240 2 /* High 240 cube (BT848) */ #define VIDEO_PALETTE_RGB565 3 /* 565 16 bit RGB */ #define VIDEO_PALETTE_RGB24 4 /* 24bit RGB */ #define VIDEO_PALETTE_RGB32 5 /* 32bit RGB */ #define VIDEO_PALETTE_RGB555 6 /* 555 15bit RGB */ #define VIDEO_PALETTE_YUV422 7 /* YUV422 capture */ #define VIDEO_PALETTE_YUYV 8 #define VIDEO_PALETTE_UYVY 9 /* The great thing about standards is ... */ #define VIDEO_PALETTE_YUV420 10 #define VIDEO_PALETTE_YUV411 11 /* YUV411 capture */ #define VIDEO_PALETTE_RAW 12 /* RAW capture (BT848) */ #define VIDEO_PALETTE_YUV422P 13 /* YUV 4:2:2 Planar */ #define VIDEO_PALETTE_YUV411P 14 /* YUV 4:1:1 Planar */ #define VIDEO_PALETTE_YUV420P 15 /* YUV 4:2:0 Planar */ #define VIDEO_PALETTE_YUV410P 16 /* YUV 4:1:0 Planar */ #define VIDEO_PALETTE_PLANAR 13 /* start of planar entries */ #define VIDEO_PALETTE_COMPONENT 7 /* start of component entries */ }; struct video_audio { int audio; /* Audio channel */ __u16 volume; /* If settable */ __u16 bass, treble; __u32 flags; #define VIDEO_AUDIO_MUTE 1 #define VIDEO_AUDIO_MUTABLE 2 #define VIDEO_AUDIO_VOLUME 4 #define VIDEO_AUDIO_BASS 8 #define VIDEO_AUDIO_TREBLE 16 #define VIDEO_AUDIO_BALANCE 32 char name[16]; #define VIDEO_SOUND_MONO 1 #define VIDEO_SOUND_STEREO 2 #define VIDEO_SOUND_LANG1 4 #define VIDEO_SOUND_LANG2 8 __u16 mode; __u16 balance; /* Stereo balance */ __u16 step; /* Step actual volume uses */ }; struct video_clip { __s32 x,y; __s32 width, height; struct video_clip *next; /* For user use/driver use only */ }; struct video_window { __u32 x,y; /* Position of window */ __u32 width,height; /* Its size */ __u32 chromakey; __u32 flags; struct video_clip *clips; /* Set only */ int clipcount; #define VIDEO_WINDOW_INTERLACE 1 #define VIDEO_WINDOW_CHROMAKEY 16 /* Overlay by chromakey */ #define VIDEO_CLIP_BITMAP -1 /* bitmap is 1024x625, a '1' bit represents a clipped pixel */ #define VIDEO_CLIPMAP_SIZE (128 * 625) }; struct video_capture { __u32 x,y; /* Offsets into image */ __u32 width, height; /* Area to capture */ __u16 decimation; /* Decimation divider */ __u16 flags; /* Flags for capture */ #define VIDEO_CAPTURE_ODD 0 /* Temporal */ #define VIDEO_CAPTURE_EVEN 1 }; struct video_buffer { void *base; int height,width; int depth; int bytesperline; }; struct video_mmap { unsigned int frame; /* Frame (0 - n) for double buffer */ int height,width; unsigned int format; /* should be VIDEO_PALETTE_* */ }; struct video_key { __u8 key[8]; __u32 flags; }; #define VIDEO_MAX_FRAME 32 struct video_mbuf { int size; /* Total memory to map */ int frames; /* Frames */ int offsets[VIDEO_MAX_FRAME]; }; #define VIDEO_NO_UNIT (-1) struct video_unit { int video; /* Video minor */ int vbi; /* VBI minor */ int radio; /* Radio minor */ int audio; /* Audio minor */ int teletext; /* Teletext minor */ }; struct vbi_format { __u32 sampling_rate; /* in Hz */ __u32 samples_per_line; __u32 sample_format; /* VIDEO_PALETTE_RAW only (1 byte) */ __s32 start[2]; /* starting line for each frame */ __u32 count[2]; /* count of lines for each frame */ __u32 flags; #define VBI_UNSYNC 1 /* can distingues between top/bottom field */ #define VBI_INTERLACED 2 /* lines are interlaced */ }; /* video_info is biased towards hardware mpeg encode/decode */ /* but it could apply generically to any hardware compressor/decompressor */ struct video_info { __u32 frame_count; /* frames output since decode/encode began */ __u32 h_size; /* current unscaled horizontal size */ __u32 v_size; /* current unscaled veritcal size */ __u32 smpte_timecode; /* current SMPTE timecode (for current GOP) */ __u32 picture_type; /* current picture type */ __u32 temporal_reference; /* current temporal reference */ __u8 user_data[256]; /* user data last found in compressed stream */ /* user_data[0] contains user data flags, user_data[1] has count */ }; /* generic structure for setting playback modes */ struct video_play_mode { int mode; int p1; int p2; }; /* for loading microcode / fpga programming */ struct video_code { char loadwhat[16]; /* name or tag of file being passed */ int datasize; __u8 *data; }; #define VIDIOCGCAP _IOR('v',1,struct video_capability) /* Get capabilities */ #define VIDIOCGCHAN _IOWR('v',2,struct video_channel) /* Get channel info (sources) */ #define VIDIOCSCHAN _IOW('v',3,struct video_channel) /* Set channel */ #define VIDIOCGTUNER _IOWR('v',4,struct video_tuner) /* Get tuner abilities */ #define VIDIOCSTUNER _IOW('v',5,struct video_tuner) /* Tune the tuner for the current channel */ #define VIDIOCGPICT _IOR('v',6,struct video_picture) /* Get picture properties */ #define VIDIOCSPICT _IOW('v',7,struct video_picture) /* Set picture properties */ #define VIDIOCCAPTURE _IOW('v',8,int) /* Start, end capture */ #define VIDIOCGWIN _IOR('v',9, struct video_window) /* Get the video overlay window */ #define VIDIOCSWIN _IOW('v',10, struct video_window) /* Set the video overlay window - passes clip list for hardware smarts , chromakey etc */ #define VIDIOCGFBUF _IOR('v',11, struct video_buffer) /* Get frame buffer */ #define VIDIOCSFBUF _IOW('v',12, struct video_buffer) /* Set frame buffer - root only */ #define VIDIOCKEY _IOR('v',13, struct video_key) /* Video key event - to dev 255 is to all - cuts capture on all DMA windows with this key (0xFFFFFFFF == all) */ #define VIDIOCGFREQ _IOR('v',14, unsigned long) /* Set tuner */ #define VIDIOCSFREQ _IOW('v',15, unsigned long) /* Set tuner */ #define VIDIOCGAUDIO _IOR('v',16, struct video_audio) /* Get audio info */ #define VIDIOCSAUDIO _IOW('v',17, struct video_audio) /* Audio source, mute etc */ #define VIDIOCSYNC _IOW('v',18, int) /* Sync with mmap grabbing */ #define VIDIOCMCAPTURE _IOW('v',19, struct video_mmap) /* Grab frames */ #define VIDIOCGMBUF _IOR('v',20, struct video_mbuf) /* Memory map buffer info */ #define VIDIOCGUNIT _IOR('v',21, struct video_unit) /* Get attached units */ #define VIDIOCGCAPTURE _IOR('v',22, struct video_capture) /* Get subcapture */ #define VIDIOCSCAPTURE _IOW('v',23, struct video_capture) /* Set subcapture */ #define VIDIOCSPLAYMODE _IOW('v',24, struct video_play_mode) /* Set output video mode/feature */ #define VIDIOCSWRITEMODE _IOW('v',25, int) /* Set write mode */ #define VIDIOCGPLAYINFO _IOR('v',26, struct video_info) /* Get current playback info from hardware */ #define VIDIOCSMICROCODE _IOW('v',27, struct video_code) /* Load microcode into hardware */ #define VIDIOCGVBIFMT _IOR('v',28, struct vbi_format) /* Get VBI information */ #define VIDIOCSVBIFMT _IOW('v',29, struct vbi_format) /* Set VBI information */ #define BASE_VIDIOCPRIVATE 192 /* 192-255 are private */ /* VIDIOCSWRITEMODE */ #define VID_WRITE_MPEG_AUD 0 #define VID_WRITE_MPEG_VID 1 #define VID_WRITE_OSD 2 #define VID_WRITE_TTX 3 #define VID_WRITE_CC 4 #define VID_WRITE_MJPEG 5 /* VIDIOCSPLAYMODE */ #define VID_PLAY_VID_OUT_MODE 0 /* p1: = VIDEO_MODE_PAL, VIDEO_MODE_NTSC, etc ... */ #define VID_PLAY_GENLOCK 1 /* p1: 0 = OFF, 1 = ON */ /* p2: GENLOCK FINE DELAY value */ #define VID_PLAY_NORMAL 2 #define VID_PLAY_PAUSE 3 #define VID_PLAY_SINGLE_FRAME 4 #define VID_PLAY_FAST_FORWARD 5 #define VID_PLAY_SLOW_MOTION 6 #define VID_PLAY_IMMEDIATE_NORMAL 7 #define VID_PLAY_SWITCH_CHANNELS 8 #define VID_PLAY_FREEZE_FRAME 9 #define VID_PLAY_STILL_MODE 10 #define VID_PLAY_MASTER_MODE 11 /* p1: see below */ #define VID_PLAY_MASTER_NONE 1 #define VID_PLAY_MASTER_VIDEO 2 #define VID_PLAY_MASTER_AUDIO 3 #define VID_PLAY_ACTIVE_SCANLINES 12 /* p1 = first active; p2 = last active */ #define VID_PLAY_RESET 13 #define VID_PLAY_END_MARK 14 #define VID_HARDWARE_BT848 1 #define VID_HARDWARE_QCAM_BW 2 #define VID_HARDWARE_PMS 3 #define VID_HARDWARE_QCAM_C 4 #define VID_HARDWARE_PSEUDO 5 #define VID_HARDWARE_SAA5249 6 #define VID_HARDWARE_AZTECH 7 #define VID_HARDWARE_SF16MI 8 #define VID_HARDWARE_RTRACK 9 #define VID_HARDWARE_ZOLTRIX 10 #define VID_HARDWARE_SAA7146 11 #define VID_HARDWARE_VIDEUM 12 /* Reserved for Winnov videum */ #define VID_HARDWARE_RTRACK2 13 #define VID_HARDWARE_PERMEDIA2 14 /* Reserved for Permedia2 */ #define VID_HARDWARE_RIVA128 15 /* Reserved for RIVA 128 */ #define VID_HARDWARE_PLANB 16 /* PowerMac motherboard video-in */ #define VID_HARDWARE_BROADWAY 17 /* Broadway project */ #define VID_HARDWARE_GEMTEK 18 #define VID_HARDWARE_TYPHOON 19 #define VID_HARDWARE_VINO 20 /* SGI Indy Vino */ #define VID_HARDWARE_CADET 21 /* Cadet radio */ #define VID_HARDWARE_TRUST 22 /* Trust FM Radio */ #define VID_HARDWARE_TERRATEC 23 /* TerraTec ActiveRadio */ #define VID_HARDWARE_CPIA 24 #define VID_HARDWARE_ZR36120 25 /* Zoran ZR36120/ZR36125 */ #define VID_HARDWARE_ZR36067 26 /* Zoran ZR36067/36060 */ #define VID_HARDWARE_OV511 27 #define VID_HARDWARE_ZR356700 28 /* Zoran 36700 series */ #define VID_HARDWARE_W9966 29 #define VID_HARDWARE_SE401 30 /* SE401 USB webcams */ #define VID_HARDWARE_PWC 31 /* Philips webcams */ #define VID_HARDWARE_MEYE 32 /* Sony Vaio MotionEye cameras */ #define VID_HARDWARE_CPIA2 33 #endif /* __LINUX_VIDEODEV_H */ /* * Local variables: * c-basic-offset: 8 * End: */ pwcbsd/pwcbsd/pwc-addons.c000644 000423 000000 00000040467 10553461760 016305 0ustar00luigiwheel000000 000000 /* * Addons for extra webcam support in the pwcbsd driver * * Copyright (C) 2007 Luigi Rizzo * * Based on the Linux pwc driver. * * Copyright (C) 1999-2003 Nemosoft Unv. * Copyright (C) 2004-2006 Luc Saillard * ... * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * 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 "pwc.h" #include "spca5xx.h" #include "hdcs.h" #include "pb0100.h" #include "vv6410.h" #include "quickcam.h" /* * Support routines extracted from the qce-ga driver */ /* * In order to identify the sensor, we read at the address specified * by id_reg, for a size of l bytes, and look for a match of the last * byte with the id specified. */ static struct sensor_data sensors [] = { /* name reg23 i2c_addr id_reg id l load */ /* ------------ ----- ----------- -------------- ---- - -------------- */ { SENSOR_HDCS1000, 0, HDCS_ADDR, HDCS_IDENT + 1, 0x08, 1, load_hdcs_mod }, { SENSOR_BP100, 1, PB_ADDR, PB_IDENT, 0x64, 2, load_pb0100_mod }, { SENSOR_VV6410, 5, VV6410_ADDR, VV6410_IDENT, 0x19, 1, load_vv6410_mod }, { SENSOR_HDCS1020, 0, HDCS_ADDR, HDCS_IDENT + 1, 0x10, 1, load_hdcs20_mod }, { -1 } }; static int usb_quickcam_i2c_in(struct usb_device *dev, int reg, unsigned char sensor_addr) { char buff[35]; /* why 35 = 23 hex? */ printf("usb_quickcam_i2c_in reg 0x%x addr 0x%x a\n", reg, sensor_addr); buff[0]=reg; buff[0x20]=sensor_addr; buff[0x21]=0; // 1 value buff[0x22]=3; // Read cmd. return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 0x04, UT_WRITE_VENDOR_DEVICE, /* UT_WRITE | UT_VENDOR | UT_DEVICE */ 0x1400, 0, buff, sizeof(buff), HZ); } /* read one byte identification register for HDCS. * write command to sensor. * read the STV0600. */ int usb_quickcam_get_i2c(struct usb_device *dev, unsigned char sensor_addr, int reg, void *buf, int len) { if (usb_quickcam_i2c_in(dev,reg,sensor_addr)<0) { printf("usb_quickcam_i2c_in failed\n"); return(-1); } return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), 0x04, UT_READ_VENDOR_DEVICE, /* UT_READ | UT_VENDOR | UT_DEVICE */ 0x1410, 0, buf, len, HZ); } /* * Set register one byte */ int usb_quickcam_set1(struct usb_device *dev, short reg, char val) { char buff[1]; buff[0] = val; return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 0x04, UT_WRITE_VENDOR_DEVICE, reg, 0, buff, 1, HZ); } /* * Set register two byte */ int usb_quickcam_set2(struct usb_device *dev, short reg, short val) { char buff[2]; buff[0] = val&0xFF; buff[1] = (val>>8)&255; return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 0x04, UT_WRITE_VENDOR_DEVICE, reg, 0, buff, 2, HZ); } /* Send a command to the sensor */ static int quickcam_usb_control_msg(struct usb_device *dev, char *buff) { return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 0x04, UT_WRITE_VENDOR_DEVICE, 0x400, 0, buff, 0x23 , HZ); } /** * Clean buffer for I2C messages. */ void usb_quickcam_i2c_new(struct quickcam_i2c *i2cbuff) { i2cbuff->length=0; memset(i2cbuff->buff,'\0',0x23); } /** * Add register and byte value. */ void usb_quickcam_i2c_add(struct quickcam_i2c *i2cbuff, unsigned char reg, unsigned char value) { i2cbuff->buff[i2cbuff->length] = reg; i2cbuff->buff[i2cbuff->length+0x10] = value; i2cbuff->length++; } /** * Add register and 2 bytes value. */ void usb_quickcam_i2c_add2(struct quickcam_i2c *i2cbuff, unsigned char reg, unsigned short value) { i2cbuff->buff[i2cbuff->length] = reg; i2cbuff->buff[(i2cbuff->length*2)+0x10] = value&255; i2cbuff->buff[(i2cbuff->length*2)+0x11] = (value>>8)&255; i2cbuff->length++; } /** * Send the I2C message. */ int usb_quickcam_i2c_send(struct usb_device *dev, struct quickcam_i2c *i2cbuff, unsigned char sensor_add) { int ret; i2cbuff->buff[0x20]=sensor_add; i2cbuff->buff[0x21]=i2cbuff->length-1; i2cbuff->buff[0x22]=1; // Write cmd, 03 would be read. ret = quickcam_usb_control_msg(dev,i2cbuff->buff); usb_quickcam_i2c_new(i2cbuff); return(ret); } int qc_probe_sensor(struct pwc_softc *sc) { struct usb_device *dev = sc->udev; struct sensor_data *sensor; unsigned char id[2]; if (dev == NULL) { /* this can happen in the probe routine */ printf("%s dev is null\n", __FUNCTION__); return 0; } if (usb_quickcam_set1(dev, STV_ISO_ENABLE, 0) < 0) { printf("usb_quickcam_set1 STV_ISO_ENABLE(0) failed\n"); return -EBUSY; } /* Probe for the sensor type. */ for (sensor = sensors; sensor->name >= 0; sensor++) { if (usb_quickcam_set1(dev, STV_REG23, sensor->reg23) < 0) { printf("usb_quickcam_set1 STV_REG23 failed\n"); return -EBUSY; } if (usb_quickcam_get_i2c(dev, sensor->i2c_addr, sensor->id_reg, id, sensor->length_id) < 0) { printf("usb_quickcam_get_i2c error\n"); return -EBUSY; } printf("quickcam: probe of sensor %d = %02x %02x id: %02x\n", sensor->name, id[0], id[1],sensor->id); if (id[sensor->length_id-1] == sensor->id) { printf("found sensor %d i2c base %d\n", sensor->name, sensor->i2c_addr); break; } } sc->pwc_info.sensor = sensor->name; sc->spca50x.i2c_base = sensor->i2c_addr; sensor->load(&(sc->sensor_ctrl) ); return 0; /* success */ } int config_quickcam(struct usb_spca50x *spca50x) { struct usb_device *dev = spca50x->dev; /* Probe for the sensor type already done */ // Disable data stream. if (usb_quickcam_set1(dev, STV_ISO_ENABLE, 0) < 0) { printf("usb_quickcam_set1 STV_ISO_ENABLE(0) failed\n"); return -EBUSY; } if (usb_quickcam_set1(dev, STV_REG23, 1) < 0) { /* I see no reason that the others failed if this one is OK */ printf("usb_quickcam_set1 STV_REG23(1) failed\n"); return -EBUSY; } memset(spca50x->mode_cam, 0x00, TOTMODE * sizeof(struct mwebcam)); spca50x->mode_cam[SIF].width = 352; spca50x->mode_cam[SIF].height = 288; spca50x->mode_cam[SIF].t_palette = P_RAW | P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[SIF].pipe = 1023; spca50x->mode_cam[SIF].method = 0; spca50x->mode_cam[CIF].width = 320; spca50x->mode_cam[CIF].height = 240; spca50x->mode_cam[CIF].t_palette = P_RAW | P_YUV420 | P_RGB32 | P_RGB24 | P_RGB16; spca50x->mode_cam[CIF].pipe = 1023; spca50x->mode_cam[CIF].method = 0; #if 0 /* set default values for the camera */ if(bright<=0 || bright>=65535) bright=EXPO_VAL; quickcam->brightness = bright; quickcam->shutter_val= SHUTTER_VAL; quickcam->scanstate = STATE_OK; quickcam->x = quickcam->yy=0; quickcam->readframe = -1; // Setup internal video_picture quickcam->vpic.hue = (rgain -bgain +0xFF) << 7; quickcam->vpic.colour = ggain; quickcam->vpic.contrast = 32768; quickcam->vpic.brightness = bright; quickcam->vpic.whiteness = 0; quickcam->vpic.palette = VIDEO_PALETTE_RGB24; quickcam->vpic.depth = quickcam_get_depth(quickcam); // Setup internal video_window quickcam->vwin.x = 0; quickcam->vwin.y = 0; quickcam->vwin.chromakey = 0; quickcam->vwin.flags = 30; /* 30 fps */ #endif printf("config_quickcam\n"); return 0; } int qc_sensor_init(struct usb_spca50x *spca50x) { static int rgain = 0, bgain = 0, ggain = 0; static int mode = 0; /* 0=normal, 1=subsample (faster) */ struct sensorctrl *sensor_ctrl = &(PWC_SC(spca50x)->sensor_ctrl) ; if (spca50x->streaming == 1) { /* stop streaming on the ISOC endpoint */ if (usb_quickcam_set1(spca50x->dev, STV_ISO_ENABLE, 0) < 0) { printf("usb_quickcam_set1 STV_ISO_ENABLE(0) failed\n"); return -EBUSY; } sensor_ctrl->stop(spca50x->dev, sensor_ctrl); } if (sensor_ctrl->init(spca50x->dev,mode, &rgain, &bgain, &ggain, sensor_ctrl)<0) { printf("qc_sensor_init failed\n"); return(-1); } #if 0 /* gain is the average of blue, green and red gain */ if(!keepexposure) { quickcam->gain =10; } quickcam->blue =qcmin(255,qcmax(2,bgain)); quickcam->red =qcmin(255,qcmax(2,rgain)); quickcam->green=qcmin(255,qcmax(2,ggain)); if (usb_quickcam_set_gains(quickcam)<0) { printk("set_gains sensor failed\n"); return(-1); } #endif if (sensor_ctrl->set_shutter(spca50x->dev, 0x80 /* val */,0x100, sensor_ctrl)<0) { printf("set_shutter sensor failed\n"); return(-1); } /* Set the size otherwise the read() will fail */ /* * JFC have to arrange this .... and use the value from the quickcam structure! * NO: It is called in open() ... For the moment... */ if (sensor_ctrl->set_size(spca50x->dev, mode)<0) { printf(" set size failed\n"); return(-1); } if (sensor_ctrl->set_window(spca50x->dev, 0, 0, 352, 288, sensor_ctrl)<0) { printf("set_window sensor failed\n"); return(-1); } /* XXX start grabbing ? */ if (sensor_ctrl->start(spca50x->dev, sensor_ctrl) < 0 ) { printf("failed to start capture\n"); return -1; } /* actually enable streaming on the ISOC endpoint */ if (usb_quickcam_set1(spca50x->dev, STV_ISO_ENABLE, 1) < 0) { printf("usb_quickcam_set1 STV_ISO_ENABLE(1) failed\n"); return -EBUSY; } return 0; } int spca561_consume(struct pwc_softc *sc, unsigned char *iso_buf, int flen) { int awake = 0; int seq; int skip_bytes; struct pwc_frame_buf *fbuf = sc->fill_frame; if (flen == 0) return 0; seq = iso_buf[SPCA50X_OFFSET_SEQUENCE]; if (seq == 0xff) return 0; /* drop packet */ if(sc->vsync != VSYNC_SYNCHED) { if (seq != 0) /* sync hunting */ return 0; sc->vsync = VSYNC_SYNCHED; printf("found sync\n"); } skip_bytes = (seq == 0) ? 10 : 1; if (seq == 0 && fbuf->filled > 0) { /* new frame */ awake = 1; pwc_next_fill_frame(sc); fbuf = sc->fill_frame; fbuf->filled = 0; } if (fbuf->filled + flen - skip_bytes > sc->frame_total_size) { /* error */ sc->vsync = VSYNC_SYNCHUNT; fbuf->filled = 0; } else { memcpy(fbuf->data + fbuf->filled, iso_buf + skip_bytes, flen - skip_bytes); fbuf->filled += flen - skip_bytes; } return awake; } int zc3xx_consume(struct pwc_softc *sc, unsigned char *iso_buf, int flen) { int awake = 0; int skip_bytes = 0; struct pwc_frame_buf *fbuf = sc->fill_frame; if (flen == 0) return 0; if (iso_buf[0] == 0xff && iso_buf[1] == 0xd8) { sc->vsync = VSYNC_SYNCHED; skip_bytes = 2; } else if (sc->vsync != VSYNC_SYNCHED) { return 0; } if (skip_bytes > 0 && fbuf->filled > 0) { /* new frame */ awake = 1; printf("%s: full frame size %d\n", __FUNCTION__, fbuf->filled); pwc_next_fill_frame(sc); fbuf = sc->fill_frame; fbuf->filled = 0; } if (fbuf->filled + flen - skip_bytes > sc->frame_total_size) { /* error */ sc->vsync = VSYNC_SYNCHUNT; fbuf->filled = 0; } else { memcpy(fbuf->data + fbuf->filled, iso_buf + skip_bytes, flen - skip_bytes); fbuf->filled += flen - skip_bytes; } return awake; } int qc_consume(struct pwc_softc *sc, unsigned char *iso_buf, int flen) { int awake = 0; /* need to awake the caller at the end */ struct pwc_frame_buf *fbuf = sc->fill_frame; unsigned char *fillptr = fbuf->data + fbuf->filled; static char buf[8192]; /* XXX fixme */ int more_data = 0; /* XXX there is no individual framestatus in FreeBSD usbstack * so just assume all frames are good */ int framesize; again: if (flen == 0) return awake; if(sc->vsync != VSYNC_SYNCHED) { /* sync hunting */ int x; switch (sc->pwc_info.bridge) { default: printf("unsupported bridge %d, cannot find sync\n", sc->pwc_info.bridge); break; case BRIDGE_SPCA561: /* 10-byte header with various info, see spca5xx.h */ printf("got %d bytes seqnum %d\n", flen, iso_buf[0]); if (iso_buf[SPCA50X_OFFSET_SEQUENCE] == 0) sc->vsync = VSYNC_SYNCHED; break; case BRIDGE_STV0602: /* see quickcam_parse_store */ if (flen < 4) return 0; for (x = 0; x < flen - 4; x++) { if ( (iso_buf[x] & ~0x40) != 0x80 || (iso_buf[x+1] & ~0x4) != 0x01 || iso_buf[x+2] != 0 || iso_buf[x+3] != 0) continue; sc->vsync = VSYNC_SYNCHED; x += 4; flen -= x; iso_buf += x; fbuf->totlength = 0; } break; } if (sc->vsync != VSYNC_SYNCHED) return awake; } /* ...copy data to frame buffer, if possible */ if (flen + fbuf->filled > sc->frame_total_size) { Trace(TRACE_ISOC, "Frame buffer overflow (flen = %d,frame_total_size = %d).\n",flen, sc->frame_total_size); goto done; // XXX try see what we get } else { memcpy(fillptr, iso_buf, flen); fillptr += flen; } fbuf->filled += flen; /* now find end of packet, if any */ switch (sc->pwc_info.bridge) { default: printf("sorry, unknown bridge %d\n", sc->pwc_info.bridge); break; case BRIDGE_STV0602: framesize = sc->image.x*sc->image.y; /* the full image */ while (fbuf->totlength < fbuf->filled - 4) { unsigned char *xx = fbuf->data + fbuf->totlength; int l = xx[2]*256 + xx[3]; if ((xx[0] & ~0x40) != 2 || xx[1] != 0 || fbuf->totlength + l >= fbuf->filled) { printf("missed header %x %x %x %x tot %d\n", xx[0], xx[1], xx[2], xx[3], fbuf->totlength); /* if we find a end-of-frame marker put a fake one */ if ((xx[0] & ~0x40) == 0x80 && (xx[1] & ~0x4) == 2) fbuf->totlength = fbuf->filled = framesize; break; } /* skip the 4 header bytes, copy the block */ memcpy(xx, xx + 4, l); fbuf->totlength += l+4; } if (fbuf->totlength >= framesize) { more_data = fbuf->filled - framesize; memcpy(buf, fbuf->data + framesize, more_data); fbuf->filled = framesize; printf("full frame left %d\n", more_data); goto done; } break; } goto eof_done; /* Shorter packet... We probably have the end of an image-frame; wake up read() process and let select()/poll() do something. Decompression is done in user time over there. */ if (sc->vsync != VSYNC_SYNCHED) goto start_new_frame; /* In case we were instructed to drop the frame, do so silently. The buffer pointers are not updated either (but the counters are reset below). */ if(sc->drop_frames > 0) { sc->drop_frames--; } /* Check for underflow first */ else if(sc->pwc_info.type != 0 && fbuf->filled < sc->frame_total_size) { Trace(TRACE_ISOC,"Frame buffer underflow (have %d bytes need %d); discarded.\n", fbuf->filled, sc->frame_total_size); sc->vframes_error++; } else { done: /* recognised frame */ /* Send only once per EOF */ awake = 1; /* delay wake_ups */ /* Find our next frame to fill. This will always succeed, since we * nick a frame from either empty or full list, but if we had to * take it from the full list, it means a frame got dropped. */ pwc_next_fill_frame(sc); fbuf = sc->fill_frame; } sc->vframe_count++; start_new_frame: fbuf->filled = 0; fillptr = fbuf->data; sc->vsync = VSYNC_SYNCHUNT; if (more_data) { flen = more_data; iso_buf = buf; more_data = 0; goto again; } eof_done: sc->vlast_packet_size = flen; return awake; } pwcbsd/pwcbsd/pwc-spca.c000644 000423 000000 00000037302 10553461760 015755 0ustar00luigiwheel000000 000000 /* * Addons for extra webcam support in the pwcbsd driver * * Copyright (C) 2007 Luigi Rizzo * * BSD License as this is totally rewritten. * * Pixart Decompressor algorithm by Bertrik.Sikken. Thomas Kaiser (C) 2005 */ #include "pwc.h" struct decompress_buf { int used; unsigned char data[2*ISO_MAX_FRAME_SIZE]; }; static int pixart_decompress_row(unsigned char *inp, unsigned char *outp, int width); /* this is public as it is called in spca5xx.c */ int pac207_consume(struct pwc_softc *sc, unsigned char *iso_buf, int flen) { int awake = 0; /* need to awake the caller at the end */ struct pwc_frame_buf *fbuf = sc->fill_frame; struct decompress_buf *buf = sc->decompress_data; while (flen > 0) { unsigned char *fillptr = fbuf->data + fbuf->filled; int i; if (sc->vsync != VSYNC_SYNCHED) { /* sync hunting */ /* * This code can support different camera types with similar * decoding/decompress requirement, so we use a switch(). */ switch (sc->pwc_info.bridge) { default: printf("unsupported bridge %d, cannot find sync\n", sc->pwc_info.bridge); break; case BRIDGE_PAC207: if (flen < 6) /* not enough to find the marker */ break; for (i = 0; i < flen - 5; i++) { static const char marker[] = { 0xff, 0xff, 0x00, 0xff, 0x96 }; if (memcmp(iso_buf + i, marker, 5) == 0) { Trace(TRACE_READ, "marker found at %d\n", i); sc->vsync = VSYNC_SYNCHED; i += 16; /* header size */ flen -= i; iso_buf += i; break; } } sc->bytes_skipped += i; } if (sc->vsync != VSYNC_SYNCHED) break; buf->used = 0; } /* copy iso_buf into decompress_data */ memcpy(buf->data + buf->used, iso_buf, flen); buf->used += flen; if (buf->used < sc->image.x + 2) /* less than one row, return */ break; /* Read the row-type and possibly decode. * After the block, i is the number of bytes used. */ i = buf->data[0] * 256 + buf->data[1]; /* row-type */ if (i == 0x1ee1) { /* compressed */ i = pixart_decompress_row(buf->data, fillptr, sc->image.x); } else if (i == 0x0ff0) { /* uncompressed, header + samples */ memcpy(fillptr, buf->data + 2, sc->image.x); i = sc->image.x + 2; /* bytes used */ } else { /* lost sync. Drop entire packet for simplicity */ printf("Invalid marker 0x%x at ofs %d, skipping\n", i, fbuf->filled); sc->vsync = VSYNC_SYNCHUNT; sc->bytes_skipped += fbuf->filled + buf->used; fbuf->filled = 0; buf->used = 0; break; } fbuf->filled += sc->image.x; /* * Make a fake buffer with the leftover data, if any, which will * be copied back into buf->data[0] at the next round. */ iso_buf = buf->data + i; flen = buf->used - i; buf->used = 0; if (fbuf->filled >= sc->image.x * sc->image.y) { /* frame ready */ if(sc->drop_frames > 0) { sc->drop_frames--; } else { /* Send only once per EOF */ awake = 1; /* delay wake_ups */ pwc_next_fill_frame(sc); fbuf = sc->fill_frame; } fbuf->filled = 0; sc->vsync = VSYNC_SYNCHUNT; } } return awake; } /* initialize variables depending on type and decompressor*/ static int spca_construct(struct pwc_softc *sc) { int i; struct usb_spca50x *spca50x = &sc->spca50x; printf("pwc_construct for spca driver bridge %d\n", sc->pwc_info.bridge); if (spca50x_configure(spca50x)) { printf("error configuring\n"); return -ENXIO; } sc->view_min.x = spca50x->minwidth; sc->view_min.x = spca50x->minheight; sc->view_max.x = spca50x->maxwidth; sc->view_max.y = spca50x->maxheight; sc->abs_max.x = spca50x->maxwidth; sc->abs_max.y = spca50x->maxheight; sc->vcinterface = 2; /* XXX correct ? */ sc->vendpoint = spca50x->vendpoint; /* XXX where is it stored ? */ sc->frame_header_size = 0; sc->frame_trailer_size = 0; /* XXX todo map formats into image_mask */ sc->image_mask = 0; for (i = QCIF; i < TOTMODE; i++) { int w = spca50x->mode_cam[i].width; int h = spca50x->mode_cam[i].height; if (w == 128 && h == 96) sc->image_mask |= 1 << PSZ_SQCIF; else if (w == 160 && h == 120) sc->image_mask |= 1 << PSZ_QSIF ; else if (w == 176 && h == 144) sc->image_mask |= 1 << PSZ_QCIF; else if (w == 192 && h == 144) sc->image_mask |= 1 << PSZ_QCIF; // XXX QPAL else if (w == 320 && h == 240) sc->image_mask |= 1 << PSZ_SIF; else if (w == 352 && h == 288) sc->image_mask |= 1 << PSZ_CIF; if (0) printf("mode %d %d WxH %d %d pipe %d method %d palette %x\n", i, spca50x->mode_cam[i].mode, spca50x->mode_cam[i].width, spca50x->mode_cam[i].height, spca50x->mode_cam[i].pipe, spca50x->mode_cam[i].method, spca50x->mode_cam[i].t_palette ); } printf("---> image_mask 0x%x\n", sc->image_mask); sc->vpalette = VIDEO_PALETTE_YUV420P; /* default */ sc->view_min.size = sc->view_min.x * sc->view_min.y; sc->view_max.size = sc->view_max.x * sc->view_max.y; /* length of image, in YUV format; always allocate enough memory. */ sc->len_per_image = (sc->abs_max.x * sc->abs_max.y * 3) / 2; return 0; } static void pixart_init_decoder(void); /* open callback for spca */ static int spca_open_cb(struct pwc_softc *sc) { sc->decompress_data = malloc(sizeof(struct decompress_buf) + PWC_FRAME_SIZE, M_USBDEV, M_WAITOK|M_ZERO); if (sc->decompress_data == NULL) return ENOMEM; /* XXX this is for the jpeg decompression */ sc->spca50x.tmpBuffer = (char *)sc->decompress_data + sizeof(struct decompress_buf); pixart_init_decoder(); /* just set a default video mode for the time being */ sc->view.x = 176; sc->view.y = 144; return 0; } /* close routine for spca */ static int spca_close_cb(struct pwc_softc *sc) { spca50x_stop_isoc(&sc->spca50x); set_alt_interface(sc->udev, sc->sc_iface, 0); return 0; } struct camera_callbacks spca_callbacks = { .cb_attach = spca_construct, .cb_open = spca_open_cb, .cb_close = spca_close_cb, .cb_decompress = spca_decompress, }; struct pixart_decode_table_t { int is_abs; int len; int val; }; /* imported from spca-decoder.c */ static inline unsigned char CLIP(int color) { return (color >0xFF) ? 0xff : ( color<0? 0 : color ) ; } // y=0.656g+0.125b+0.226r static inline unsigned char RGB24_TO_Y(int r, int g, int b) { return CLIP((g*656+b*125+r*226)/1000); // return CLIP((((g) <<9)+((g)<<7)+((g)<<5)+((b)<<7)+((r)<<8)-((r)<<4)-((r)<<3))>>10); } // v=(r-y)0.656 // #define YR_TO_V(r,y) CLIP( 128 + (((((r)-(y)) << 9 )+(((r)-(y)) << 7 )+(((r)-(y)) << 5 )) >> 10)) static inline unsigned char YR_TO_V(int r, int y) { return CLIP( 128 + ( ((r - y) * 656)/1000) ) ; // return CLIP( 128 + (((((r)-(y)) << 9 )+(((r)-(y)) << 7 )+(((r)-(y)) << 5 )) >> 10)) } // u=(b-y)0.5 //#define YB_TO_U(b,y) CLIP(128 + (((b)-(y)) >> 1)) static inline unsigned char YB_TO_U(int b, int y) { return CLIP( 128 + (b-y)/2 ); } /* XXX hooks for gamma correction */ #define Red(x) x #define Green(x) x #define Blue(x) x static struct pixart_decode_table_t pixart_decode_table[256]; /* pixart compressed format handler */ static void pixart_init_decoder(void) { int i; int is_abs, val, len; struct pixart_decode_table_t *table = pixart_decode_table; if (table[0].len != 0) /* already initialized */ return; for (i = 0; i < 256; i++) { is_abs = 0; val = 0; len = 0; if ((i & 0xC0) == 0) { /* code 00 */ val = 0; len = 2; } else if ((i & 0xC0) == 0x40) { /* code 01 */ val = -5; len = 2; } else if ((i & 0xC0) == 0x80) { /* code 10 */ val = +5; len = 2; } else if ((i & 0xF0) == 0xC0) { /* code 1100 */ val = -10; len = 4; } else if ((i & 0xF0) == 0xD0) { /* code 1101 */ val = +10; len = 4; } else if ((i & 0xF8) == 0xE0) { /* code 11100 */ val = -15; len = 5; } else if ((i & 0xF8) == 0xE8) { /* code 11101 */ val = +15; len = 5; } else if ((i & 0xFC) == 0xF0) { /* code 111100 */ val = -20; len = 6; } else if ((i & 0xFC) == 0xF4) { /* code 111101 */ val = +20; len = 6; } else if ((i & 0xF8) == 0xF8) { /* code 11111xxxxxx */ is_abs = 1; val = 0; len = 5; } table[i].is_abs = is_abs; table[i].val = val; table[i].len = len; } } /* * support routine for pixart decoding. * The input buffer start at 'base', we read 8 bit at * bit offset 'bitpos'. */ static inline unsigned char getByte(unsigned char *base, unsigned int bitpos) { unsigned char *addr = base + (bitpos >> 3); return (addr[0] << (bitpos & 7)) | (addr[1] >> (8 - (bitpos & 7))); } /* * Decompress routine. * First two bytes are the identifier (0x1e, 0xe1) * followed bu two pixels as raw 8 bit, followed by compressed samples * (stored as huffman-compressed deltas). * Because the format is bayer, the first two pixels are RY or BY, * and all the following pixels are deltas with respect to the * corresponding R or Y sample. */ static int pixart_decompress_row(unsigned char *inp, unsigned char *outp, int width) { int col; int val; int bitpos; unsigned char code; struct pixart_decode_table_t *table = pixart_decode_table; /* first two pixels are stored as raw 8-bit */ *outp++ = inp[2]; *outp++ = inp[3]; bitpos = 32; /* skip the 1e e1 marker and the first 2 pixels */ /* main decoding loop */ for (col = 2; col < width; col++) { /* get bitcode */ code = getByte(inp, bitpos); bitpos += table[code].len; /* calculate pixel value */ if (table[code].is_abs) { /* absolute value: get 6 more bits */ code = getByte(inp, bitpos); bitpos += 6; *outp++ = code & 0xFC; } else { /* relative to left pixel */ val = outp[-2] + table[code].val; *outp++ = CLIP(val); } } /* return line length, rounded up to next 16-bit word */ val = 2 * ((bitpos + 15) / 16); #if 0 printf("decompress skipped %d bytes\n", val); for (col = 0; col < 2*val; col++) { if (inp[col] == 0x1e && inp[col+1] == 0xe1) printf("0x1ee1 at offset %d\n", col); } #endif return val; } /* convert skipping ofs bytes at the beginning of each line */ static void bayer_to_yuv(unsigned char *dst, unsigned char *buf, int width, int height, int padx, int pady, int ofs) { int mx, my; /* loop on input */ int outwidth = width - 2*padx; int outheight = height - 2*pady; int framesize = outwidth * outheight; /* output frame size */ unsigned char *U = dst + framesize, *V = U + framesize/4; unsigned char *pic = dst; unsigned char * pic1 = pic + outwidth; int nextinline = width*2; int inl = 0, inl1 = width; /* input pointers for line 0 and 1 */ // buf += ofs; /* XXX offset by 1 ? mess on the last pixel ? */ /* * We have the following pixel layout, which we scan two rows at a time. * We focus on the center square, while the borders are not available * so we have to come up with fake values for them. * If a sample is not available we use the average of the * surrounding samples for the same color component. * The algorithm below is called Bilinear Interpolation and is one with * the best cost/performance ratio. */ for (my = 0; my < height; my += 2) { if (my < pady || my >= pady + outheight) continue; for (mx = 0; mx < width; mx += 2) { unsigned char r00, g10, r20, g30; unsigned char g01, b11, g21, b31; unsigned char r02, g12, r22, g32; unsigned char g03, b13, g23, b33; /* completion of the central matrix */ unsigned char y11, y21, y12, y22; unsigned char g11, g22; unsigned char r11, r21, r12; unsigned char b21, b12, b22; unsigned char y, r, b; if (mx < padx || mx >= padx + outwidth) continue; b11 = buf[inl + mx + 0]; g21 = buf[inl + mx + 1]; g12 = buf[inl1 + mx + 0]; r22 = buf[inl1 + mx + 1]; if (mx == 0 || my == 0 || mx == width - 2 || my == height - 2) { /* assume the whole border is not available */ /* XXX could be done better because some * of the samples do exist, but do it later. */ g11 = g22 = (g21 + g12) >> 1; b21 = b12 = b22 = b11; r11 = r21 = r12 = r22; } else { r00 = buf[inl + mx - width - 1]; g10 = buf[inl + mx - width ]; r20 = buf[inl + mx - width + 1]; g30 = buf[inl + mx - width + 2]; g01 = buf[inl + mx - 1]; b31 = buf[inl + mx + 2]; r02 = buf[inl1 + mx - 1]; g32 = buf[inl1 + mx + 2]; g03 = buf[inl1 + mx + width - 1]; b13 = buf[inl1 + mx + width ]; g23 = buf[inl1 + mx + width + 1]; b33 = buf[inl1 + mx + width + 2]; g11 = (g10 + g01 + g21 + g12) >> 2; g22 = (g32 + g23 + g21 + g12) >> 2; b21 = (b11 + b31) >> 1; b12 = (b11 + b13) >> 1; b22 = (b11 + b31 + b13 + b33) >> 2; r11 = (r00 + r20 + r02 + r22) >> 2; r12 = (r02 + r22) >> 1; r21 = (r20 + r22) >> 1; } r = (r11 + r12 + r21 + r22) >> 2; b = (b11 + b12 + b21 + b22) >> 2; *pic++ = y11 = RGB24_TO_Y(r11, g11, b11); *pic++ = y21 = RGB24_TO_Y(r21, g21, b21); *pic1++ = y12 = RGB24_TO_Y(r12, g12, b12); *pic1++ = y22 = RGB24_TO_Y(r22, g22, b22); y = (y11 + y21 + y12 + y22) >> 2; *U++ = YB_TO_U(b, y); *V++ = YR_TO_V(r, y); } /* end mx loop */ inl += nextinline; inl1 += nextinline; pic += outwidth; pic1 += outwidth; } // end my loop } int spca_decompress(struct pwc_softc *pdev, unsigned char *src, unsigned char *image, int srclen) { int i; char *dst = src; char *base = src; struct spca50x_frame myframe; switch (pdev->pwc_info.dataformat) { default: printf("unrecognised dataformat %d, returning raw data\n", pdev->pwc_info.bridge); memcpy(image, src, pdev->frame_size); return 0; case JPGH: { /* Logitech ? */ int w = (src[10] << 8 ) + src[11]; int h = (src[12] << 8 ) + src[13]; printf("found jpgh %d x %d bytes %d\n", w, h, srclen); /* XXX check width */ #if 1 myframe.decoder = &pdev->spca50x.maindecode; myframe.tmpbuffer = pdev->spca50x.tmpBuffer; /* XXX malloc ? */ if (myframe.tmpbuffer == NULL) { printf("%s: buffer not allocated\n", __FUNCTION__); return -ENOMEM; } /* the decoder decodes in-place... */ memcpy(image, src, pdev->frame_size); myframe.data = image; myframe.x = myframe.width = myframe.hdrwidth = pdev->image.x; myframe.y = myframe.height = myframe.hdrheight = pdev->image.y; myframe.cameratype = pdev->spca50x.cameratype; myframe.pictsetting = pdev->spca50x.pictsetting; myframe.format = pdev->spca50x.format; myframe.method = 0; /* no crop, no pad... */ myframe.cropx1 = myframe.cropx2 = 0; myframe.cropy1 = myframe.cropy2 = 0; myframe.scanlength = pdev->image.x * pdev->image.y * 3/2; /* assume 420 data */ spca50x_outpicture(&myframe); #endif break; } case S561: /* Creative */ #if 0 if (src[1] & 0x10) { decode_spca561(myframe->data, myframe->tmpbuffer, myframe->width, myframe->height); } else #endif memcpy(src, src + 10, pdev->image.x * pdev->image.y); bayer_to_yuv(image, src, pdev->image.x, pdev->image.y, (pdev->image.x - pdev->view.x)/2, (pdev->image.y - pdev->view.y)/2, 1); break; case NONE: /* Dexxa camera */ /* XXX copy in place */ printf("bytes %d size %d x %d\n", srclen, pdev->image.x, pdev->image.y); for (i=0; i < pdev->image.y; i++) { memcpy(dst, src, pdev->image.x); src += pdev->image.x + 16; dst += pdev->image.x; } /* XXX not really */ bayer_to_yuv(image, base, pdev->image.x, pdev->image.y, (pdev->image.x - pdev->view.x)/2, (pdev->image.y - pdev->view.y)/2, 0); break; case PGBRG: /* pixart bayer */ bayer_to_yuv(image, base, pdev->image.x, pdev->image.y, (pdev->image.x - pdev->view.x)/2, (pdev->image.y - pdev->view.y)/2, 1); break; } return 0; } pwcbsd/pwcbsd/.#pwc.h.1.26000644 000423 000000 00000033560 10551705277 015550 0ustar00luigiwheel000000 000000 /* * PWCBSD - Philips USB webcam driver for FreeBSD 5.4 and higher * * Copyright (C) 2006 Raaf * * Based on the Linux pwc driver. * * Copyright (C) 1999-2003 Nemosoft Unv. * Copyright (C) 2004-2006 Luc Saillard * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * 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 */ #ifndef PWC_H #define PWC_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pwc-uncompress.h" #include "pwc-ioctl.h" #include "videodev.h" #include "linux_usbif.h" #include "spca_globals.h" #include "spca5xx.h" #define pwc_device pwc_softc #define PWC_ASLEEP 1 #define PWC_INIT 2 #define PWC_POLL 4 /* Trace certain actions in the driver */ #define TRACE_MODULE 0x0001 #define TRACE_PROBE 0x0002 #define TRACE_OPEN 0x0004 #define TRACE_READ 0x0008 #define TRACE_MEMORY 0x0010 #define TRACE_FLOW 0x0020 #define TRACE_SIZE 0x0040 #define TRACE_PWCX 0x0080 #define TRACE_IOCTL 0x0100 #define TRACE_ISOC 0x0200 #define TRACE_ISOC_VERBOSE 0x0400 #define TRACE_READ_VERBOSE 0x0800 #define TRACE_SEQUENCE 0x1000 #ifdef USB_DEBUG #define Trace(R, fmt , A...) if (pwcdebug & R) printf(PWC_NAME " %d " fmt , __LINE__ , ## A) #define Debug(fmt , A...) if(pwcdebug) printf("%s:%d " PWC_NAME " " fmt , __FUNCTION__, __LINE__ , ##A) // extern int pwcdebug; #else #define Trace(R, A...) #define Debug(A...) #endif #define Info(A...) printf(PWC_NAME " " A) #define Err(A...) printf(PWC_NAME " " A) /* Defines for ToUCam cameras */ #define TOUCAM_HEADER_SIZE 8 #define TOUCAM_TRAILER_SIZE 4 #define FEATURE_MOTOR_PANTILT 0x0001 #define PWC_NAME "pwc" /* Ignore errors in the first N frames, to allow for startup delays */ #define FRAME_LOWMARK 5 /* Size and number of buffers for the ISO pipe. */ #define MAX_ISO_BUFS 3 /* !!!!!!!!!! IMPORTANT DO NOT SET THIS HIGHER THAN 8 !!!!!!!!!!!!!!! * !!!!!!!!!! OHCI IS BUGGY IF YOU DO SO !!!!!!!!!!!!!!!*/ #define ISO_FRAMES_PER_DESC 8 /* XXX was 8 */ #define ISO_MAX_FRAME_SIZE (3*1024) /* XXX was 960 */ #define ISO_BUFFER_SIZE (ISO_FRAMES_PER_DESC * ISO_MAX_FRAME_SIZE) /* Frame buffers: contains compressed or uncompressed video data. */ #define MAX_FRAMES 5 /* Maximum size after decompression is 640x480 YUV data, 1.5 * 640 * 480 */ #define PWC_FRAME_SIZE (460800 + TOUCAM_HEADER_SIZE + TOUCAM_TRAILER_SIZE) /* Absolute maximum number of buffers available for mmap() */ #define MAX_IMAGES 10 #ifndef V4L2_PIX_FMT_PWC1 #define V4L2_PIX_FMT_PWC1 v4l2_fourcc('P','W','C','1') #define V4L2_PIX_FMT_PWC2 v4l2_fourcc('P','W','C','2') #endif struct pwc_softc; struct pwc_iso_buf { unsigned char *data; usbd_xfer_handle _xfer; /* XXX move away from here */ u_int16_t sizes[ISO_FRAMES_PER_DESC]; struct pwc_softc *sc; }; typedef int (consume_t)(struct pwc_softc *sc, unsigned char *buf, int flen); /* camera callbacks */ struct camera_cb { int probe(struct pwc_softc *sc, void *arg); int attach(struct pwc_softc *sc, void *arg); int open(struct pwc_softc *sc, void *arg); int close(struct pwc_softc *sc, void *arg); consume_t *consume; int set_mode(struct pwc_softc *sc, void *arg); int start(struct pwc_softc *sc, void *arg); int stop(struct pwc_softc *sc, void *arg); int ioctl(struct pwc_softc *sc, void *arg); }; /* camera descriptor - all info we might need. */ struct pwc_info { struct usb_devno devno; int type; const char *name; struct camera_cb *callbacks; int bridge; int sensor; int altinterface; int videoendpoint; }; /* intermediate buffers with raw data from the USB cam */ struct pwc_frame_buf { char *data; volatile int filled; /* number of bytes filled */ struct pwc_frame_buf *next; /* list */ /* spca fields, not really used but necessary for compiling */ int last_packet; char *highwater; int totlength; /* XXX next position to look for */ int scanstate; int grabstate; int format; }; /* additionnal informations used when dealing image between kernel and userland */ struct pwc_imgbuf { char *bufmem; /* addr of this buffer in kernel space */ int vma_use_count; /* number of times this memory is mapped */ }; /* * State machine for vsync. NOCOPY means we are hunting for the * initial SYNC, which happens when we detect an end-of-frame * (in the original pwc driver, a transfer shorter than the previous one). * SYNCHUNT is set after the end of frame, and means we can start * copying the data, and enter the SYNCHED state as soon as we have * a non-zero frame. At the end of the frame we move to SYNCHUNT. */ enum { VSYNC_NOCOPY, VSYNC_SYNCHUNT, VSYNC_SYNCHED, }; struct pwc_softc { device_t sc_dev; usbd_device_handle udev; usbd_interface_handle sc_iface; usbd_pipe_handle sc_videopipe; struct cdev *sc_dev_t; struct selinfo rsel; const char *name; int type; /* type of cam (645, 646, 675, 680, 690, 720, 730, 740, 750) */ int release; /* release number */ int features; /* feature bits */ char serial[USB_MAX_STRING_LEN]; /* serial number (string) */ int error_status; /* set when something goes wrong with the cam (unplugged, USB errors) */ int usb_init; /* set when the cam has been initialized over USB */ struct pwc_info *pwc_info; /* backpointer to the descriptor */ struct pwc_info _this_pwc_info; /* points here if not found outside */ int bridge; /* bridge type */ int sensor; /* sensor type */ int power_save; int led_on; int led_off; int stats; int pwc_mbufs; int pwc_fbufs; int pwc_pad; consume_t *consume; /* data consumer routine */ /*** Video data ***/ int vopen; /* flag */ int vendpoint; /* video isoc endpoint */ int vcinterface; /* video control interface */ int valternate; /* alternate interface needed */ int vframes, vsize; /* frames-per-second & size (see PSZ_*) */ int vpalette; /* palette: 420P, RAW or RGBBAYER */ int vframe_count; /* received frames */ int vframes_dumped; /* counter for dumped frames */ int vframes_error; /* frames received in error */ int vmax_packet_size; /* USB maxpacket size */ u_int16_t vlast_packet_size; /* for frame synchronisation */ int visoc_errors; /* number of contiguous ISOC errors */ int vcompression; /* desired compression factor */ int vbandlength; /* compressed band length; 0 is uncompressed */ char vsnapshot; /* snapshot mode */ char vsync; /* used by isoc handler */ char vmirror; /* for ToUCaM series */ int cmd_len; unsigned char cmd_buf[13]; /* The image acquisition requires 3 to 4 steps: 1. data is gathered in short packets from the USB controller 2. data is synchronized and packed into a frame buffer 3a. in case data is compressed, decompress it directly into image buffer 3b. in case data is uncompressed, copy into image buffer with viewport 4. data is transferred to the user process Note that MAX_ISO_BUFS != MAX_FRAMES != MAX_IMAGES.... We have in effect a back-to-back-double-buffer system. */ /* 1: isoc */ usbd_xfer_handle xfer[MAX_ISO_BUFS]; /* XXX NEW_USB uses this */ #ifndef NEW_USB struct pwc_iso_buf sbuf[MAX_ISO_BUFS]; #endif char iso_init; /* 2: frame */ struct pwc_frame_buf *fbuf; /* all frames */ struct pwc_frame_buf *empty_frames, *empty_frames_tail; /* all empty frames */ struct pwc_frame_buf *full_frames, *full_frames_tail; /* all filled frames */ struct pwc_frame_buf *fill_frame; /* frame currently being filled */ struct pwc_frame_buf *read_frame; /* frame currently read by user process */ int frame_header_size, frame_trailer_size; int frame_size; int frame_total_size; /* including header & trailer */ int drop_frames; /* 3: decompression */ void *decompress_data; /* private data for decompression engine */ /* 4: image */ /* We have an 'image' and a 'view', where 'image' is the fixed-size image as delivered by the camera, and 'view' is the size requested by the program. The camera image is centered in this viewport, laced with a gray or black border. view_min <= image <= view <= view_max; In other word, the driver does padding, not truncation. */ int image_mask; /* bitmask of supported sizes */ struct pwc_coord view_min, view_max; /* minimum and maximum viewable sizes */ struct pwc_coord abs_max; /* maximum supported size with compression */ struct pwc_coord image, view; /* image and viewport size */ struct pwc_coord offset; /* offset within the viewport */ void *image_data; /* total buffer, which is subdivided into ... */ struct pwc_imgbuf images[MAX_IMAGES];/* ...several images... */ int fill_image; /* ...which are rotated. */ int len_per_image; /* length per image */ int image_read_pos; /* In case we read data in pieces, keep track of were we are in the imagebuffer */ int image_used[MAX_IMAGES]; /* For MCAPTURE and SYNC */ struct mtx ptrlock; /* for manipulating the buffer pointers */ /*** motorized pan/tilt feature */ struct pwc_mpt_range angle_range; int pan_angle; /* in degrees * 100 */ int tilt_angle; /* absolute angle; 0,0 is home position */ int snapshot_button_status; /* set to 1 when the user push the button, reset to 0 when this value is read */ /*** Misc. data ***/ int state; /* When waiting for a frame to finish... */ /* XXX Finally, spwc support */ struct usb_spca50x spca50x; struct sensorctrl sensor_ctrl; }; #ifdef __cplusplus extern "C" { #endif /** functions in pwc.c */ int pwc_next_fill_frame(struct pwc_softc *sc); /** functions in pwc-if.c */ int pwc_try_video_mode(struct pwc_device *pdev, int width, int height, int new_fps, int new_compression, int new_snapshot); int pwc_handle_frame(struct pwc_device *pdev); /** Functions in pwc-misc.c */ /* sizes in pixels */ struct pwc_format { struct pwc_coord xy; /* width * height */ int id; /* format identifier, PSZ_* */ const char *name; /* format name */ }; extern struct pwc_format pwc_image_sizes[PSZ_MAX]; int pwc_decode_size(struct pwc_device *pdev, int width, int height); void pwc_construct(struct pwc_device *pdev); /** Functions in pwc-ctrl.c */ /* Request a certain video mode. Returns < 0 if not possible */ extern int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, int frames, int compression, int snapshot); /* Calculate the number of bytes per image (not frame) */ extern int pwc_mpt_reset(struct pwc_device *pdev, int flags); extern int pwc_mpt_set_angle(struct pwc_device *pdev, int pan, int tilt); /* Various controls; should be obvious. Value 0..65535, or < 0 on error */ extern int pwc_get_brightness(struct pwc_device *pdev); extern int pwc_set_brightness(struct pwc_device *pdev, int value); extern int pwc_get_contrast(struct pwc_device *pdev); extern int pwc_set_contrast(struct pwc_device *pdev, int value); extern int pwc_get_gamma(struct pwc_device *pdev); extern int pwc_set_gamma(struct pwc_device *pdev, int value); extern int pwc_get_saturation(struct pwc_device *pdev); extern int pwc_set_saturation(struct pwc_device *pdev, int value); extern int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value); extern int pwc_get_leds(struct pwc_device *pdev, int *on_value, int *off_value); extern int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor); extern int pwc_restore_user(struct pwc_device *pdev); extern int pwc_save_user(struct pwc_device *pdev); extern int pwc_restore_factory(struct pwc_device *pdev); /* exported for use by v4l2 controls */ extern int pwc_get_red_gain(struct pwc_device *pdev, int *value); extern int pwc_set_red_gain(struct pwc_device *pdev, int value); extern int pwc_get_blue_gain(struct pwc_device *pdev, int *value); extern int pwc_set_blue_gain(struct pwc_device *pdev, int value); extern int pwc_get_awb(struct pwc_device *pdev); extern int pwc_set_awb(struct pwc_device *pdev, int mode); extern int pwc_set_agc(struct pwc_device *pdev, int mode, int value); extern int pwc_get_agc(struct pwc_device *pdev, int *value); extern int pwc_set_shutter_speed(struct pwc_device *pdev, int mode, int value); extern int pwc_get_shutter_speed(struct pwc_device *pdev,int *value); extern int pwc_set_colour_mode(struct pwc_device *pdev, int colour); extern int pwc_get_colour_mode(struct pwc_device *pdev, int *colour); /* Power down or up the camera; not supported by all models */ extern int pwc_camera_power(struct pwc_device *pdev, int power); /* Private ioctl()s; see pwc-ioctl.h */ extern int pwc_do_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg); /** Functions in pwc-v4l.c */ extern int pwc_video_do_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg, int unit); /** pwc-uncompress.c */ /* Expand frame to image, possibly including decompression. Uses read_frame and fill_image */ extern int pwc_decompress(struct pwc_device *pdev); /** in pwc-addons.c */ int qc_consume(struct pwc_softc *sc, unsigned char *buf, int flen); /** in pwc-spca.c */ int pac207_consume(struct pwc_softc *sc, unsigned char *buf, int flen); #ifdef __cplusplus } #endif #endif pwcbsd/pwcbsd/pwc-info.c000644 000423 000000 00000026111 10553461760 015756 0ustar00luigiwheel000000 000000 /* * Misc info for webcam support in the pwcbsd driver * * Copyright (C) 2007 Luigi Rizzo * * BSD license as is this has been rewritten. */ #include "pwc.h" struct id_desc { int id; char *desc; }; static struct id_desc sensor_list[] = { { SENSOR_UNKNOWN, "Unknown sensor" }, { SENSOR_SAA7113, "SAA 7113" }, { SENSOR_INTERNAL, "Integrated sensor" }, { SENSOR_HV7131B, "HV7131B" }, { SENSOR_HDCS1020, "HDCS1020" }, { SENSOR_PB100_BA, "PB100 BA" }, { SENSOR_PB100_92, "PB100 92" }, { SENSOR_PAS106_80, "PAS 106-80" }, { SENSOR_TAS5130C, "TAS 5130C" }, { SENSOR_ICM105A, "ICM 105A" }, { SENSOR_HDCS2020, "HDC S2020" }, { SENSOR_PAS106, "PAS 106" }, { SENSOR_PB0330, "PB 0330" }, { SENSOR_HV7131C, "HV 7131C" }, { SENSOR_CS2102, "CS 2102" }, { SENSOR_HDCS2020b, "HDCS 2020b" }, { SENSOR_HV7131R, "HV 7131R" }, { SENSOR_OV7630, "OV 7630" }, { SENSOR_MI0360, "MI 0360" }, { SENSOR_TAS5110, "TAS 5110" }, { SENSOR_PAS202, "PAS 202" }, { SENSOR_PAC207, "PAC 207 (Pixart)" }, /* CIF sensor from Pixart */ { SENSOR_OV7630C, "OV 7630C" }, { SENSOR_HDCS1000, "HDCS 1000 (Quickcam Express)" }, { SENSOR_BP100, "BP100 (same as PB 100?)" }, { SENSOR_VV6410, "VV 6410" }, { SENSOR_PAS302, "PAS 302" }, { SENSOR_PAS207, "PAS 207, same as PAC207 ?" }, /* various sensors used on the pwc cameras */ { SENSOR_PWC_HYUNDAI, "Hyundai CMOS (pwc)" }, { SENSOR_PWC_TDA8787, "TDA 8787 (pwc)" }, { SENSOR_PWC_SONY_Exas98L59, "Sony + Exas 98L59" }, { SENSOR_PWC_SONY_ADI9804, "Sony + ADI 9804" }, { SENSOR_PWC_SHARP_TDA8787, "Sharp + TDA 8787" }, { SENSOR_PWC_SHARP_Exas98L59, "Sharp + Exas 98L59" }, { SENSOR_PWC_SHARP_ADI9804, "Sharp + ADI 9804" }, { SENSOR_PWC_UPA1021, "UPA 1021" }, { SENSOR_PWC_VGA, "PWC VGA sensor" }, { SENSOR_PWC_PAL_MR, "PWC PAL sensor" }, { SENSOR_PWC_UNKNOWN, "PWC, unknown sensor" }, { -1, NULL } }; static struct id_desc bridge_list[] = { {BRIDGE_SPCA505, "SPCA505"}, {BRIDGE_SPCA506, "SPCA506"}, {BRIDGE_SPCA501, "SPCA501"}, {BRIDGE_SPCA508, "SPCA508"}, {BRIDGE_SPCA504, "SPCA504"}, {BRIDGE_SPCA500, "SPCA500"}, {BRIDGE_SPCA504B, "SPCA504B"}, {BRIDGE_SPCA533, "SPCA533"}, {BRIDGE_SPCA504C, "SPCA504C"}, {BRIDGE_SPCA561, "SPCA561"}, {BRIDGE_SPCA536, "SPCA536"}, {BRIDGE_SONIX, "SN9C102"}, {BRIDGE_ZC3XX, "ZC301-2"}, {BRIDGE_CX11646, "CX11646"}, {BRIDGE_TV8532, "TV8532"}, {BRIDGE_ETOMS, "ET61XX51"}, {BRIDGE_SN9CXXX, "SN9CXXX"}, {BRIDGE_MR97311, "MR97311"}, {BRIDGE_PAC207, "PAC207BCA"}, {BRIDGE_STV0602, "STV0602"}, {BRIDGE_EM2820, "EM2710/2820"}, {-1, NULL} }; static struct id_desc camera_list[] = { {UnknownCamera, "Unknown"}, {IntelPCCameraPro, "Intel PC Camera Pro"}, {IntelCreateAndShare, "Intel Create and Share"}, {GrandtecVcap, "Grandtec V.cap"}, {ViewQuestM318B, "ViewQuest M318B"}, {ViewQuestVQ110, "ViewQuest VQ110"}, {KodakDVC325, "Kodak DVC-325"}, {MustekGsmartMini2, "Mustek gSmart mini 2"}, {MustekGsmartMini3, "Mustek gSmart mini 3"}, {CreativePCCam300, "Creative PC-CAM 300"}, {DLinkDSC350, "D-Link DSC-350"}, {CreativePCCam600, "Creative PC-CAM 600"}, {IntelPocketPCCamera, "Intel Pocket PC Camera"}, {IntelEasyPCCamera, "Intel Easy PC Camera"}, {ThreeComHomeConnectLite, "3Com Home Connect Lite"}, {KodakEZ200, "Kodak EZ200"}, {MaxellMaxPocket, "Maxell Max Pocket LEdit. 1.3 MPixels"}, {AiptekMiniPenCam2, "Aiptek Mini PenCam 2 MPixels"}, {AiptekPocketDVII, "Aiptek PocketDVII 1.3 MPixels"}, {AiptekPenCamSD, "Aiptek Pencam SD 2 MPixels"}, {AiptekMiniPenCam13, "Aiptek mini PenCam 1.3 MPixels"}, {MustekGsmartLCD3, "Mustek Gsmart LCD 3"}, {MustekMDC5500Z, "Mustek MDC5500Z"}, {MegapixV4, "Megapix V4"}, {AiptekPocketDV, "Aiptek PocketDV "}, {HamaUSBSightcam, "Hama USB Sightcam 100"}, {Arowana300KCMOSCamera, "Arowana 300K CMOS Camera"}, {MystFromOriUnknownCamera, "Unknow Ori Camera"}, {AiptekPocketDV3100, "Aiptek PocketDV3100+ "}, {AiptekPocketCam3M, "Aiptek PocketCam 3 M "}, {GeniusVideoCAMExpressV2, "Genius VideoCAM Express V2"}, {Flexcam100Camera, "Flexcam 100 Camera"}, {MustekGsmartLCD2, "Mustek Gsmart LCD 2"}, {PureDigitalDakota, "Pure Digital Dakota"}, {PetCam, "PetCam"}, {BenqDC1500, "Benq DC1500"}, {LogitechClickSmart420, "Logitech Inc. ClickSmart 420"}, {LogitechClickSmart510, "Logitech Inc. ClickSmart 510"}, {BenqDC1300, "Benq DC1300"}, {HamaUSBSightcam2, "Hama USB Sightcam 100 (2)"}, {MustekDV3000, "Mustek DV 3000"}, {CreativePccam750, "Creative PCcam750"}, {MaxellCompactPM3, "Maxell Compact PC PM3"}, {BenqDC3410, "Benq DC3410"}, {BenqDC1016, "Benq DC1016"}, {MicroInnovationIC200, "Micro Innovation IC200"}, {LogitechTraveler, "Logitech QuickCam Traveler"}, {Flycam100Camera, "FlyCam Usb 100"}, {UsbGrabberPV321c, "Usb Grabber PV321c"}, {ADSInstantVCD, "ADS Instant VCD"}, {Gsmartmini, "Mustek Gsmart Mini"}, {Jenoptikjdc21lcd, "Jenoptik DC 21 LCD"}, {LogitechClickSmart310, "Logitech ClickSmart 310"}, {Terratec2move13, "Terratec 2 move 1.3"}, {MustekDV4000, "Mustek DV4000 Mpeg4"}, {AiptekDV3500, "Aiptek DV3500 Mpeg4"}, {LogitechClickSmart820, "Logitech ClickSmart 820"}, {Enigma13, "Digital Dream Enigma 1.3"}, {Sonix6025, "Xcam Shanga"}, {Epsilon13, "Digital Dream Epsilon 1.3"}, {Nxultra, "Creative Webcam NX ULTRA"}, {AiptekPocketCam2M, "Aiptek PocketCam 2Mega"}, {DeMonUSBCapture, "3DeMON USB Capture"}, {CreativeVista, "Creative Webcam Vista"}, {PolaroidPDC2030, "Polaroid PDC2030"}, {CreativeNotebook, "Creative Notebook PD1171"}, {CreativeMobile, "Creative Mobile PD1090"}, {LabtecPro, "Labtec Webcam Pro"}, {MustekWcam300A, "Mustek Wcam300A"}, {GeniusVideoCamV2, "Genius Videocam V2"}, {GeniusVideoCamV3, "Genius Videocam V3"}, {GeniusVideoCamExpressV2b, "Genius Videocam Express V2 Firmware 2"}, {CreativeNxPro, "Creative Nx Pro"}, {Sonix6029, "Sonix sn9c10x + Pas106 sensor"}, {Vimicro, "Z-star Vimicro zc0301p"}, {Digitrex2110, "ApexDigital Digitrex2110 spca533"}, {GsmartD30, "Mustek Gsmart D30 spca533"}, {CreativeNxPro2, "Creative NX Pro FW2"}, {Bs888e, "Kowa Bs888e MicroCamera"}, {Zc302, "Z-star Vimicro zc0302"}, {CreativeNoteBook2, "Creative Notebook PD1170"}, {AiptekSlim3200, "Aiptek Slim 3200"}, {LabtecWebcam, "Labtec Webcam"}, {QCExpress, "QC Express"}, {ICM532cam, "ICM532 cam"}, {MustekGsmart300, "Mustek Gsmart 300"}, {CreativeLive, "Creative Live! "}, {MercuryDigital, "Mercury Digital Pro 3.1Mp"}, {Wcam300A, "Mustek Wcamm300A 2"}, {CreativeVista3b, "Creative Webcam Vista 0x403b"}, {VeoStingray1, "Veo Stingray 1"}, {VeoStingray2, "Veo Stingray 2"}, {TyphoonWebshotIIUSB300k, " Typhoon Webshot II"}, {PolaroidPDC3070, " Polaroid PDC3070"}, {QCExpressEtch2, "Logitech QuickCam Express II"}, {QCforNotebook, "Logitech QuickCam for Notebook"}, {QCim, "Logitech QuickCam IM"}, {WebCam320, "Micro Innovation WebCam 320"}, {AiptekPocketCam4M, "Aiptek Pocket Cam 4M"}, {AiptekPocketDV5100, "Aiptek Pocket DV5100"}, {AiptekPocketDV5300, "Aiptek Pocket DV5300"}, {SunplusGeneric536, "Sunplus Generic spca536a"}, {QCimA1, "Logitech QuickCam IM + sound"}, {QCchat, "Logitech QuickCam chat"}, {QCimB9, "Logitech QuickCam IM ???"}, {Labtec929, "Labtec Webcam Elch2 "}, {Etoms61x151, "QCam Sangha"}, {Etoms61x251, "QCam xxxxxx"}, {PalmPixDC85, "PalmPix DC85"}, {Optimedia, "Optimedia TechnoAME"}, {ToptroIndus, "Toptro Industrial"}, {AgfaCl20, "Agfa ephoto CL20"}, {LogitechQC92c, "Logitech QuickCam chat"}, {SonixWC311P, "Sonix sn9c102P Hv7131R"}, {Concord3045, "Concord 3045 spca536a"}, {Mercury21, "Mercury Peripherals Inc."}, {CreativeNX, "Creative NX"}, {CreativeInstant1, "Creative Instant P0620"}, {CreativeInstant2, "Creative Instant P0620D"}, {QuickCamNB, "Logitech QuickCam for Notebooks"}, {WCam300AN, "Mustek WCam300AN "}, {LabtecWCPlus, "Labtec Webcam Plus"}, {GeniusVideoCamMessenger, "VideoCam Messenger sn9c101 Ov7630"}, {Pcam, "Mars-Semi Pc-Camera MR97311 MI0360"}, {GeniusDsc13, "Genius Dsc 1.3 Smart spca504B-P3"}, {MustekMDC4000, "Mustek MDC4000"}, {LogitechQCCommunicateSTX, "Logitech QuickCam Communicate STX"}, {Lic200, "LG LIC-200"}, {SweexTas5110, "Sweex SIF webcam"}, {Pccam168, "Sonix PcCam"}, {Sn535, "Sangha 350k"}, {Pccam, "Sonix Pccam +"}, {Lic300, "LG Lic-300"}, {PolaroidIon80, "Polaroid Ion 80"}, {Zc0305b, "Generic Zc0305b"}, {BtcPc380, "Sonix Btc PC380"}, {LogitechNotebookDeluxe, "Logitech Notebook Deluxe"}, {LabtecNotebook, "Labtec Webcam Notebook"}, {JvcGcA50, "JVC GC-A50"}, {SmileIntlCamera, "Smile International"}, {PcCam350, "PC-Cam350"}, {PAC207, "Pixart PAC207-BCA"}, {QtecWb100, "Qtec Webcam 100"}, {GeniusGe111, "Genius VideoCam Ge111"}, {Vimicro303b, "Generic Vimicro 303b"}, {CyberpixS550V, "Mercury Cyberpix S550V"}, {GeniusGF112, "Genius GF112"}, {LogitechQCim, "Logitech QCIM"}, {AiptekSlim3000F, "Aiptek Slim3000F"}, {CTXM730VCam, "CTX M730V built in Cam"}, {GeniusVideoCamNB, "Genius VideoCAM NB"}, {CreativeVistaPlus, "Creative Webcam Vista Plus"}, {PhilipsSPC200NC, "Philips SPC200NC "}, {PhilipsSPC700NC, "Philips SPC700NC "}, {SpeedNVC350K, "Speed NVC 350K "}, {Mustek330K, "Mustek Digicam 330K "}, {PhilipsSPC600NC, "Philips SPC600NC "}, {PhilipsSPC300NC, "Philips SPC300NC "}, {Sonix6019, "Sonix VGA Ov7630 "}, {LogitechQCImage, "Logitech QuickCam Image "}, {Sunplus500c, "Sunplus CA500C "}, {MustekMDC3500, "Mustek MDC3500"}, {LogitechQCCool, "Logitech QuickCam Cool"}, {QCimconnect, "Logitech QuickCam IM/Connect "}, {QCmessenger, "Logitech QuickCam Messenger "}, {CreativeLiveCamNotebookPro, "Creative Live!Cam Notebook Pro (VF0250)"}, {CreativeWebCamVistaPro, "Creative WebCam Vista Pro"}, {CreativeLiveCamVideoIM, "Creative Live Cam Video IM"}, {AiptekDV4100M, "Aiptek DV4100M"}, {TyphoonEasyCam1_3, "Typhoon Easy Cam 1.3 MPix"}, {Sonix0x613b, "Surfer model sn-206"}, {Sonix0x60fb, "Surfer model noname"}, {Sonyc002,"Vc0321"}, {Vimicro0321,"Vc0321"}, {Orbicam,"Logitech Orbicam"}, {M$VX1000,"MicroSoft VX1000"}, {Trust610LCDPowerCamZoom, "Trust 610 LCD PowerC@m Zoom"}, {Sonyc001,"Sony Visual Communication VGP-VCC1"}, {DexxaWebcam, "Dexxa/Labtec cam "}, {SilverCrest, "Silvercrest 1.3M "}, {-1, NULL} }; static const char * map_key(int key, struct id_desc *p, const char *def) { int i; for (i = 0; p[i].desc != NULL; i++) if (p[i].id == key) return p[i].desc; return def; } /* * Returns the description string for a given sensor or bridge index */ const char * sensor_name(int sensor) { return map_key(sensor, sensor_list, "Unknown sensor"); } const char * bridge_name(int bridge) { return map_key(bridge, bridge_list, "Unknown bridge"); } const char * camera_name(int camera) { return map_key(camera, camera_list, "Unknown camera"); } pwcbsd/pwcbsd/pwc-info.h000644 000423 000000 00000015202 10553461760 015762 0ustar00luigiwheel000000 000000 /* * Description of bridges, cameras, sensors * * Copyright (C) 2007 Luigi Rizzo * * BSD license as is this has been rewritten. */ #ifndef PWC_INFO_H #define PWC_INFO_H enum { BRIDGE_UNKNOWN = 0, BRIDGE_SPCA505, BRIDGE_SPCA506, BRIDGE_SPCA501, BRIDGE_SPCA508, BRIDGE_SPCA504, BRIDGE_SPCA500, BRIDGE_SPCA504B, BRIDGE_SPCA533, BRIDGE_SPCA504C, BRIDGE_SPCA561, BRIDGE_SPCA536, BRIDGE_SONIX, BRIDGE_ZC3XX, BRIDGE_CX11646, BRIDGE_TV8532, BRIDGE_ETOMS, BRIDGE_SN9CXXX, BRIDGE_MR97311, BRIDGE_PAC207, BRIDGE_STV0600, BRIDGE_STV0610, /* "LEGO cam" */ BRIDGE_STV0602, /* "Dexxa Webcam" */ BRIDGE_EM2820, /* "Silvercrest - also 2820" */ BRIDGE_SN9C201, /* Trust wb5500 ? */ BRIDGE_LAST }; enum { SENSOR_UNKNOWN = 0, SENSOR_SAA7113, SENSOR_INTERNAL, SENSOR_HV7131B, SENSOR_HDCS1020, SENSOR_PB100_BA, SENSOR_PB100_92, SENSOR_PAS106_80, SENSOR_TAS5130C, /* XXX also SENSOR_TAS5130CXX */ SENSOR_ICM105A, SENSOR_HDCS2020, SENSOR_PAS106, SENSOR_PB0330, SENSOR_HV7131C, SENSOR_CS2102, SENSOR_HDCS2020b, SENSOR_HV7131R, SENSOR_OV7630, SENSOR_MI0360, SENSOR_TAS5110, SENSOR_PAS202, SENSOR_PAC207, /* CIF sensor from Pixart */ SENSOR_OV7630C, SENSOR_HDCS1000, /* Quickcam express */ SENSOR_BP100, /* XXX same as PB100 ? */ SENSOR_VV6410, SENSOR_PAS302, SENSOR_PAS207, /* various sensors used on the pwc cameras */ SENSOR_PWC_HYUNDAI, SENSOR_PWC_TDA8787, SENSOR_PWC_SONY_Exas98L59, SENSOR_PWC_SONY_ADI9804, SENSOR_PWC_SHARP_TDA8787, SENSOR_PWC_SHARP_Exas98L59, SENSOR_PWC_SHARP_ADI9804, SENSOR_PWC_UPA1021, SENSOR_PWC_VGA, SENSOR_PWC_PAL_MR, SENSOR_PWC_UNKNOWN, SENSOR_TAS5130C_VF0250, SENSOR_MO4000, SENSOR_OV7660, SENSOR_PO3130NC, SENSOR_LAST }; /* Camera type jpeg yuvy yyuv yuyv grey gbrg*/ enum dataformat{ NONE = 0, //Jpeg 4.1.1 Sunplus JPEG, //Jpeg 4.1.1 Sunplus JPGH, //jpeg 4.2.2 Zstar JPGC, //jpeg 4.2.2 Conexant JPGS, //jpeg 4.2.2 Sonix JPGM, //jpeg 4.2.2 Mars-Semi YUVY, YYUV, YUYV, GREY, GBRG, SN9C, // Sonix compressed stream GBGR, S561, // Sunplus Compressed stream PGBRG, // Pixart RGGB bayer }; /* Camera identifiers */ enum { UnknownCamera = 0, // 0 IntelPCCameraPro, IntelCreateAndShare, GrandtecVcap, ViewQuestM318B, ViewQuestVQ110, KodakDVC325, MustekGsmartMini2, MustekGsmartMini3, CreativePCCam300, DLinkDSC350, // 10 CreativePCCam600, IntelPocketPCCamera, IntelEasyPCCamera, ThreeComHomeConnectLite, KodakEZ200, MaxellMaxPocket, AiptekMiniPenCam2, AiptekPocketDVII, AiptekPenCamSD, AiptekMiniPenCam13, // 20 MustekGsmartLCD3, MustekMDC5500Z, MegapixV4, AiptekPocketDV, HamaUSBSightcam, Arowana300KCMOSCamera, MystFromOriUnknownCamera, AiptekPocketDV3100, AiptekPocketCam3M, GeniusVideoCAMExpressV2, // 30 Flexcam100Camera, MustekGsmartLCD2, PureDigitalDakota, PetCam, BenqDC1500, LogitechClickSmart420, LogitechClickSmart510, BenqDC1300, HamaUSBSightcam2, MustekDV3000, // 40 CreativePccam750, MaxellCompactPM3, BenqDC3410, BenqDC1016, MicroInnovationIC200, LogitechTraveler, Flycam100Camera, UsbGrabberPV321c, ADSInstantVCD, Gsmartmini, // 50 Jenoptikjdc21lcd, LogitechClickSmart310, Terratec2move13, MustekDV4000, AiptekDV3500, LogitechClickSmart820, Enigma13, Sonix6025, Epsilon13, Nxultra, //60 AiptekPocketCam2M, DeMonUSBCapture, CreativeVista, PolaroidPDC2030, CreativeNotebook, CreativeMobile, LabtecPro, MustekWcam300A, GeniusVideoCamV2, GeniusVideoCamV3, GeniusVideoCamExpressV2b, CreativeNxPro, Sonix6029, //73 74 75 Vimicro, Digitrex2110, GsmartD30, CreativeNxPro2, Bs888e, Zc302, CreativeNoteBook2, AiptekSlim3200, /* 83 84 85 */ LabtecWebcam, QCExpress, ICM532cam, MustekGsmart300, CreativeLive, //90 MercuryDigital, Wcam300A, CreativeVista3b, VeoStingray1, VeoStingray2, TyphoonWebshotIIUSB300k, //96 PolaroidPDC3070, QCExpressEtch2, QCforNotebook, QCim, //100 WebCam320, AiptekPocketCam4M, AiptekPocketDV5100, AiptekPocketDV5300, SunplusGeneric536, QCimA1, QCchat, QCimB9, Labtec929, //109 110 Etoms61x151, Etoms61x251, PalmPixDC85, Optimedia, ToptroIndus, AgfaCl20, LogitechQC92c, SonixWC311P, Concord3045, Mercury21, //120 CreativeNX, CreativeInstant1, CreativeInstant2, QuickCamNB, WCam300AN, LabtecWCPlus, GeniusVideoCamMessenger, Pcam, GeniusDsc13, MustekMDC4000, //130 LogitechQCCommunicateSTX, Lic200, SweexTas5110, Pccam168, Sn535, Pccam, Lic300, PolaroidIon80, Zc0305b, BtcPc380, //140 LogitechNotebookDeluxe, LabtecNotebook, JvcGcA50, SmileIntlCamera, PcCam350, PAC207, QtecWb100, GeniusGe111, Vimicro303b, CyberpixS550V, GeniusGF112, LogitechQCim, AiptekSlim3000F, CTXM730VCam, GeniusVideoCamNB, CreativeVistaPlus, PhilipsSPC200NC, PhilipsSPC700NC, SpeedNVC350K, Mustek330K, PhilipsSPC600NC, PhilipsSPC300NC, Sonix6019, LogitechQCImage, Sunplus500c, MustekMDC3500, LogitechQCCool, QCimconnect, QCmessenger, CreativeLiveCamNotebookPro, CreativeWebCamVistaPro, CreativeLiveCamVideoIM, AiptekDV4100M, TyphoonEasyCam1_3, Sonix0x613b, Sonix0x60fb, Sonyc002, Vimicro0321, Orbicam, M$VX1000, Trust610LCDPowerCamZoom, Sonyc001, DexxaWebcam, /* XXX from qce-ga */ SilverCrest, TrustWB5500, LastCamera }; /* * Returns the description string for a given sensor or bridge index */ const char * sensor_name(int sensor); const char * bridge_name(int bridge); const char * camera_name(int camera); #endif /* !PWC_INFO_H */ pwcbsd/pwcbsd/pwc.h000644 000423 000000 00000040776 10553463446 015052 0ustar00luigiwheel000000 000000 /* * PWCBSD - Philips USB webcam driver for FreeBSD 5.4 and higher * * Copyright (C) 2006 Raaf * * Based on the Linux pwc driver. * * Copyright (C) 1999-2003 Nemosoft Unv. * Copyright (C) 2004-2006 Luc Saillard * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * 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 */ #ifndef PWC_H #define PWC_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "linux_usbif.h" #include "pwc-info.h" #define pwc_device pwc_softc /** various forward declarations */ struct pwc_softc; struct usb_device; #include "spca5xx.h" #include "pwc-uncompress.h" #include "pwc-ioctl.h" #include "videodev.h" #include "spca_globals.h" #define PWC_ASLEEP 1 #define PWC_INIT 2 #define PWC_POLL 4 /* Trace certain actions in the driver */ #define TRACE_MODULE 0x0001 #define TRACE_PROBE 0x0002 #define TRACE_OPEN 0x0004 #define TRACE_READ 0x0008 #define TRACE_MEMORY 0x0010 #define TRACE_FLOW 0x0020 #define TRACE_SIZE 0x0040 #define TRACE_PWCX 0x0080 #define TRACE_IOCTL 0x0100 #define TRACE_ISOC 0x0200 #define TRACE_ISOC_VERBOSE 0x0400 #define TRACE_READ_VERBOSE 0x0800 #define TRACE_SEQUENCE 0x1000 #ifdef USB_DEBUG #define Trace(R, fmt , A...) if (pwcdebug & R) printf(PWC_NAME " %d " fmt , __LINE__ , ## A) #define Debug(fmt , A...) if(pwcdebug) printf("%s:%d " PWC_NAME " " fmt , __FUNCTION__, __LINE__ , ##A) // extern int pwcdebug; #else #define Trace(R, A...) #define Debug(A...) #endif #define Info(A...) printf(PWC_NAME " " A) #define Err(A...) printf(PWC_NAME " " A) /* Defines for ToUCam cameras */ #define TOUCAM_HEADER_SIZE 8 #define TOUCAM_TRAILER_SIZE 4 #define FEATURE_MOTOR_PANTILT 0x0001 #define PWC_NAME "pwc" /* Ignore errors in the first N frames, to allow for startup delays */ #define FRAME_LOWMARK 5 /* Size and number of buffers for the isochronous pipe. */ #define MAX_ISOC_TRANSFERS 3 /* !!!!!!!!!! IMPORTANT DO NOT SET THIS HIGHER THAN 8 !!!!!!!!!!!!!!! * !!!!!!!!!! OHCI IS BUGGY IF YOU DO SO !!!!!!!!!!!!!!!*/ #define ISO_FRAMES_PER_DESC 8 /* XXX was 8 */ #define ISO_MAX_FRAME_SIZE (1024) /* XXX was 960 */ #define ISO_BUFFER_SIZE (ISO_FRAMES_PER_DESC * ISO_MAX_FRAME_SIZE) /* Frame buffers: contains compressed or uncompressed video data. */ #define MAX_FRAMES 5 /* Maximum size after decompression is 640x480 YUV data, 1.5 * 640 * 480 */ /* XXX update to 640x480x4... */ #define PWC_FRAME_SIZE (640*480*4 + TOUCAM_HEADER_SIZE + TOUCAM_TRAILER_SIZE) /* Absolute maximum number of buffers available for mmap() */ #define MAX_IMAGES 10 #ifndef V4L2_PIX_FMT_PWC1 #define V4L2_PIX_FMT_PWC1 v4l2_fourcc('P','W','C','1') #define V4L2_PIX_FMT_PWC2 v4l2_fourcc('P','W','C','2') #endif struct pwc_iso_buf { unsigned char *data; u_int16_t sizes[ISO_FRAMES_PER_DESC]; struct pwc_softc *sc; }; /* * Callbacks for the various cameras */ /* * Things to do on attach i.e. after a probe has been successful. * Typically, fill in the supported modes, talk to the hardware to * detect camera features, reset leds, power save and so on. * Also, set a default video mode (or is this generic ?) */ typedef int (attach_cb_t)(struct pwc_softc *sc); /* * Things to do on open. * Typically exit from power save, turn on leds, set a fallback format, * allocate decompress data etc. * Remember, the video format is not reset among opens(?) */ typedef int (open_cb_t)(struct pwc_softc *sc); typedef int (setmode_cb_t)(struct pwc_softc *sc); typedef int (start_cb_t)(struct pwc_softc *sc); typedef int (stop_cb_t)(struct pwc_softc *sc); typedef int (ioctl_cb_t)(struct pwc_softc *sc); typedef int (close_cb_t)(struct pwc_softc *sc); typedef int (consume_cb_t)(struct pwc_softc *sc, unsigned char *buf, int flen); typedef int (decompress_cb_t)(struct pwc_softc *sc, unsigned char *dst, unsigned char *src, int srclen); struct camera_callbacks { attach_cb_t *cb_attach; open_cb_t *cb_open; setmode_cb_t *cb_setmode; start_cb_t *cb_start; stop_cb_t *cb_stop; ioctl_cb_t *cb_ioctl; close_cb_t *cb_close; consume_cb_t *cb_consume; decompress_cb_t *cb_decompress; }; /* camera descriptor - all info we might need. */ struct pwc_info { struct usb_devno devno; int type; const char *name; struct camera_callbacks *cb; int bridge; int sensor; int dataformat; int altinterface; int videoendpoint; }; /* intermediate buffers with raw data from the USB cam */ struct pwc_frame_buf { char *data; volatile int filled; /* number of bytes filled */ struct pwc_frame_buf *next; /* list */ /* spca fields, not really used but necessary for compiling */ int last_packet; char *highwater; int totlength; /* XXX camera-specific */ int scanstate; int grabstate; int format; }; /* additionnal informations used when dealing image between kernel and userland */ struct pwc_imgbuf { char *bufmem; /* addr of this buffer in kernel space */ int vma_use_count; /* number of times this memory is mapped */ }; /* * State machine for vsync. NOCOPY means we are hunting for the * initial SYNC, which happens when we detect an end-of-frame * (in the original pwc driver, a transfer shorter than the previous one). * SYNCHUNT is set after the end of frame, and means we can start * copying the data, and enter the SYNCHED state as soon as we have * a non-zero frame. At the end of the frame we move to SYNCHUNT. */ enum { VSYNC_NOCOPY, VSYNC_SYNCHUNT, VSYNC_SYNCHED, }; struct pwc_softc { device_t sc_dev; usbd_device_handle udev; usbd_interface_handle sc_iface; usbd_pipe_handle sc_videopipe; struct cdev *sc_dev_t; struct selinfo rsel; // const char *name; //int type; /* type of cam (645, 646, 675, 680, 690, 720, 730, 740, 750) */ int release; /* release number */ int features; /* feature bits */ char serial[USB_MAX_STRING_LEN]; /* serial number (string) */ int error_status; /* set when something goes wrong with the cam (unplugged, USB errors) */ int usb_init; /* set when the cam has been initialized over USB */ struct pwc_info pwc_info; /* local copy of pwc_info, with fixes if needed */ struct camera_callbacks camera_cb; /* local copy of pwc_info, with fixes if needed */ int frames; /* frames read so far */ int start_ticks; /* frames read so far */ int byte_count; /* total bytes read */ int bytes_skipped; /* bytes skipped sync-hunting */ int power_save; int led_on; int led_off; int stats; int pwc_mbufs; int pwc_fbufs; int pwc_pad; // consume_cb_t *consume; /* data consumer routine */ /*** Video data ***/ int vopen; /* flag */ int vendpoint; /* video isoc endpoint */ int vcinterface; /* video control interface */ int valternate; /* alternate interface needed */ int vframes, vsize; /* frames-per-second & size (see PSZ_*) */ int vpalette; /* palette: 420P, RAW or RGBBAYER */ int vframe_count; /* received frames */ int vframes_dumped; /* counter for dumped frames */ int vframes_error; /* frames received in error */ int vmax_packet_size; /* USB maxpacket size */ u_int16_t vlast_packet_size; /* for frame synchronisation */ int visoc_errors; /* number of contiguous ISOC errors */ int vcompression; /* desired compression factor */ int vbandlength; /* compressed band length; 0 is uncompressed */ char vsnapshot; /* snapshot mode */ char vsync; /* used by isoc handler */ char vmirror; /* for ToUCaM series */ int cmd_len; unsigned char cmd_buf[13]; /* The image acquisition requires 3 to 4 steps: 1. data is gathered in short packets from the USB controller. We have up to MAX_ISOC_TRANSFERS pending usb transfers, each one transferring up to ISO_FRAMES_PER_DESC * ISO_MAX_FRAME_SIZE (FRAME here refers to usb frames). One transfer is not necessarily one video frame, it depends on image size and compression mode and a few more issues. However, we know that one (usb) frame is transferred in 1ms (full speed) or 123us (high speed) so we can tune the transfer size (i.e. the number of frames) so that interrupt occur every 30ms i.e. at least once per video frame. Faster than that is useless, slower means unnecessary frame delay. 2. data is synchronized and packed into a frame buffer. We have MAX_FRAME video frame buffers, filled in the interrupt service routine with the data from the ISOC buffers. In most cases we need to identify synchronization markers indicating the beginning of the (video) frame, and do an initial decoding of the data stream to identify when a full video frame is available. 3a. in case data is compressed, decompress it directly into image buffer 3b. in case data is uncompressed, copy into image buffer with viewport 4. data is transferred to the user process Note that MAX_ISOC_TRANSFERS != MAX_FRAMES != MAX_IMAGES.... We have in effect a back-to-back-double-buffer system. */ /* 1: isoc */ usbd_xfer_handle xfer[MAX_ISOC_TRANSFERS]; /* XXX NEW_USB uses this */ #ifndef NEW_USB struct pwc_iso_buf sbuf[MAX_ISOC_TRANSFERS]; #endif char iso_init; /* 2: frame */ struct pwc_frame_buf *fbuf; /* all frames */ struct pwc_frame_buf *empty_frames, *empty_frames_tail; /* all empty frames */ struct pwc_frame_buf *full_frames, *full_frames_tail; /* all filled frames */ struct pwc_frame_buf *fill_frame; /* frame currently being filled */ struct pwc_frame_buf *read_frame; /* frame currently read by user process */ int frame_header_size, frame_trailer_size; int frame_size; int frame_total_size; /* including header & trailer */ int drop_frames; /* 3: decompression */ void *decompress_data; /* private data for decompression engine */ /* 4: image */ /* We have an 'image' and a 'view', where 'image' is the fixed-size image as delivered by the camera, and 'view' is the size requested by the program. The camera image is centered in this viewport, laced with a gray or black border. view_min <= image <= view <= view_max; In other word, the driver does padding, not truncation. */ int image_mask; /* bitmask of supported sizes */ struct pwc_coord view_min, view_max; /* minimum and maximum viewable sizes */ struct pwc_coord abs_max; /* maximum supported size with compression */ struct pwc_coord image, view; /* image and viewport size */ struct pwc_coord offset; /* offset within the viewport */ void *image_data; /* total buffer, which is subdivided into ... */ struct pwc_imgbuf images[MAX_IMAGES];/* ...several images... */ int fill_image; /* ...which are rotated. */ int len_per_image; /* length per image */ int image_read_pos; /* In case we read data in pieces, keep track of were we are in the imagebuffer */ int image_used[MAX_IMAGES]; /* For MCAPTURE and SYNC */ struct mtx ptrlock; /* for manipulating the buffer pointers */ /*** motorized pan/tilt feature */ struct pwc_mpt_range angle_range; int pan_angle; /* in degrees * 100 */ int tilt_angle; /* absolute angle; 0,0 is home position */ int snapshot_button_status; /* set to 1 when the user push the button, reset to 0 when this value is read */ /*** Misc. data ***/ int state; /* When waiting for a frame to finish... */ /* XXX Finally, spwc support */ struct usb_spca50x spca50x; struct sensorctrl sensor_ctrl; }; #ifdef __cplusplus extern "C" { #endif /** functions in pwc.c */ int pwc_next_fill_frame(struct pwc_softc *sc); /** functions in pwc-if.c */ int pwc_try_video_mode(struct pwc_device *pdev, int width, int height, int new_fps, int new_compression, int new_snapshot); int pwc_handle_frame(struct pwc_device *pdev); /** Functions in pwc-misc.c */ /* sizes in pixels */ struct pwc_format { struct pwc_coord xy; /* width * height */ int id; /* format identifier, PSZ_* */ const char *name; /* format name */ }; extern struct pwc_format pwc_image_sizes[]; int set_alt_interface(struct usbd_device *udev, usbd_interface_handle iface, int alt); int usb_control_msg(usbd_device_handle udev, u_int pipe,u_int8_t request,u_int8_t requesttype, u_int16_t value, u_int16_t index, void *data, u_int16_t size,int timeout); struct pwc_format * pwc_decode_size(struct pwc_device *pdev, int width, int height); /* * show existing configuration. * If alt and ep are != -1, return the endpoint for that configuration. */ usb_endpoint_descriptor_t * pwc_show_configs(struct pwc_softc *sc, int alt, int ep); /* callbacks for the various devices */ extern struct camera_callbacks pwc_callbacks; extern struct camera_callbacks spca_callbacks; /** Functions in pwc-ctrl.c */ /* Request a certain video mode. Returns < 0 if not possible */ extern int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, int frames, int compression, int snapshot); /* Calculate the number of bytes per image (not frame) */ extern int pwc_mpt_reset(struct pwc_device *pdev, int flags); extern int pwc_mpt_set_angle(struct pwc_device *pdev, int pan, int tilt); /* Various controls; should be obvious. Value 0..65535, or < 0 on error */ extern int pwc_get_brightness(struct pwc_device *pdev); extern int pwc_set_brightness(struct pwc_device *pdev, int value); extern int pwc_get_contrast(struct pwc_device *pdev); extern int pwc_set_contrast(struct pwc_device *pdev, int value); extern int pwc_get_gamma(struct pwc_device *pdev); extern int pwc_set_gamma(struct pwc_device *pdev, int value); extern int pwc_get_saturation(struct pwc_device *pdev); extern int pwc_set_saturation(struct pwc_device *pdev, int value); extern int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value); extern int pwc_get_leds(struct pwc_device *pdev, int *on_value, int *off_value); extern int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor); extern int pwc_restore_user(struct pwc_device *pdev); extern int pwc_save_user(struct pwc_device *pdev); extern int pwc_restore_factory(struct pwc_device *pdev); /* exported for use by v4l2 controls */ extern int pwc_get_red_gain(struct pwc_device *pdev, int *value); extern int pwc_set_red_gain(struct pwc_device *pdev, int value); extern int pwc_get_blue_gain(struct pwc_device *pdev, int *value); extern int pwc_set_blue_gain(struct pwc_device *pdev, int value); extern int pwc_get_awb(struct pwc_device *pdev); extern int pwc_set_awb(struct pwc_device *pdev, int mode); extern int pwc_set_agc(struct pwc_device *pdev, int mode, int value); extern int pwc_get_agc(struct pwc_device *pdev, int *value); extern int pwc_set_shutter_speed(struct pwc_device *pdev, int mode, int value); extern int pwc_get_shutter_speed(struct pwc_device *pdev,int *value); extern int pwc_set_colour_mode(struct pwc_device *pdev, int colour); extern int pwc_get_colour_mode(struct pwc_device *pdev, int *colour); /* Power down or up the camera; not supported by all models */ extern int pwc_camera_power(struct pwc_device *pdev, int power); /* Private ioctl()s; see pwc-ioctl.h */ extern int pwc_do_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg); /** Functions in pwc-v4l.c */ extern int pwc_video_do_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg, int unit); /** pwc-uncompress.c */ /* Expand frame to image, possibly including decompression. Uses read_frame and fill_image */ decompress_cb_t pwc_decompress; decompress_cb_t spca_decompress; /** in pwc-addons.c */ int zc3xx_consume(struct pwc_softc *sc, unsigned char *buf, int flen); int spca561_consume(struct pwc_softc *sc, unsigned char *buf, int flen); int qc_consume(struct pwc_softc *sc, unsigned char *buf, int flen); int qc_probe_sensor(struct pwc_softc *sc); /** in pwc-spca.c */ int pac207_consume(struct pwc_softc *sc, unsigned char *buf, int flen); #ifdef __cplusplus } #endif #endif pwcbsd/usb/dsbr100io.h000644 000423 000000 00000004112 10551670753 015252 0ustar00luigiwheel000000 000000 /*- * Copyright (c) 2001 M. Warner Losh * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * This code is based on ugen.c and ulpt.c developed by Lennart Augustsson. * This code includes software developed by the NetBSD Foundation, Inc. and * its contributors. */ /* $FreeBSD: src/sys/dev/usb/dsbr100io.h,v 1.2 2005/01/06 01:36:28 imp Exp $ */ #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) #include #endif #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) #define FM_SET_FREQ _IOWR('U', 200, int) #define FM_GET_FREQ _IOWR('U', 201, int) #define FM_START _IOWR('U', 202, int) #define FM_STOP _IOWR('U', 203, int) #define FM_GET_STAT _IOWR('U', 204, int) #else #define FM_SET_FREQ 0x1 #define FM_GET_FREQ 0x2 #define FM_START 0x3 #define FM_STOP 0x4 #define FM_GET_STAT 0x5 #endif pwcbsd/usb/README000644 000423 000000 00000030065 10551670753 014264 0ustar00luigiwheel000000 000000 DESCRIPTION OF THE NEW USB API The new USB 2.0 API consists of 4 functions. All transfer types are managed using these functions. There is no longer need for separate functions to setup INTERRUPT- and ISOCHRONOUS- transfers. +--------------------------------------------------------------+ | | | "usbd_transfer_setup" - This function will allocate all | | necessary DMA memory and might | | sleep! | | | | "usbd_transfer_unsetup" - This function will stop the USB | | transfer, if it is currently | | active and release all DMA | | memory. | | | | "usbd_transfer_start" - This function will start a USB | | transfer, if not already started.| | This function is always non- | | blocking, except when the USB | | transfer is SYNCHRONOUS. ** | | | | "usbd_transfer_stop" - This function will stop a USB | | transfer, if not already stopped.| | The callback function will be | | called before this function | | returns. This function is always | | non-blocking. ** | | | | ** These functions must be called with the private driver's | | lock locked. | | | +--------------------------------------------------------------+ Reference: /sys/dev/usb/usb_transfer.c One must setup the USB transfer, struct usbd_xfer, from the callback handler, which is required for non-blocking operation, in the end. This behavior is also very practical when writing USB device drivers, because it is easy to make a loop, starting the next transfer from the previous. Simply reorder the labels! The callback's lock is locked by the caller. This solves synchronization problems related to stopping USB transfers. /* * A simple USB callback state-machine: * * +->-----------------------+ * | | * +-<-+-------[tr_setup]--------+-<-+-<-[start/restart] * | | * | | * | | * +------>-[tr_transferred]---------+ * | | * +--------->-[tr_error]------------+ */ void usbd_default_callback(struct usbd_xfer *xfer) { /* NOTE: it is not allowed to return * before "USBD_CHECK_STATUS()", * even if the system is tearing down! */ USBD_CHECK_STATUS(xfer); tr_setup: /* setup xfer->length, xfer->frlengths, xfer->nframes * and write data to xfer->buffer if any */ /**/ usbd_start_hardware(xfer); return; tr_transferred: /* read data from xfer->buffer if any */ tr_error: /* print error message */ return; } NOTE for control endpoints: all of the control transfer now resides in the buffer pointed to by "xfer->buffer", including the request. This is better and saves memory. 1) Something that one should be aware of is that, all USB callbacks support recursation. That means one can start/stop whatever transfer from the callback of another transfer one desires. Also the transfer that is currently called back. Recursion is handled like this, that when the callback that wants to recurse returns, it is called one more time. 2) After that the "tr_setup" label has been jumped in the callback, one can always depend on that "tr_error" or "tr_transferred" will get jumped afterwards. Always! 3) sleeping functions can only be called from the attach routine of the driver. Else one should not use sleeping functions unless one has to. It is very difficult with sleep, because one has to think that the device might have detached when it returns from sleep. USB device driver examples: /sys/dev/usb/ugen.c /sys/dev/usb/ulpt.c /sys/dev/usb/uhid.c /sys/dev/usb/... QUICK REFERENCE =============== /*------------------------------------------------------------------------* * usbd_status * usbd_transfer_setup(udev, iface_index, pxfer, setup_start, * n_setup, priv_sc, priv_mtx) *------------------------------------------------------------------------*/ - "udev" is a pointer to "struct usbd_device" - "iface_index" is the interface index number - "pxfer" is a pointer to an array of USB transfer pointers that are initialized to NULL, and then pointed to the allocated DMA-able USB transfers - "setup_start" is a pointer to an array of USB config structures - "n_setup" is a number telling the USB system how many USB transfers should be setup - "priv_sc" is the private softc pointer, which will be used to initialize "xfer->priv_sc" - "priv_mtx" is the private mutex protecting the transfer structure and the softc. This pointer is used to initialize "xfer->priv_mtx". /*------------------------------------------------------------------------* * void * usbd_transfer_unsetup(pxfer, n_setup) *------------------------------------------------------------------------*/ - "pxfer" is a pointer to an array of USB transfer pointers, that may be NULL, that should be freed by the USB system. - "n_setup" is a number telling the USB system how many USB transfers should be unsetup NOTE: This function can sleep, waiting for active mutexes to become unlocked! NOTE: It is not allowed to call "usbd_transfer_unsetup" from the callback of a USB transfer. /*------------------------------------------------------------------------* * void * usbd_transfer_start(xfer) *------------------------------------------------------------------------*/ - "xfer" is pointer to a USB transfer that should be started NOTE: this function must be called with "priv_mtx" locked /*------------------------------------------------------------------------* * void * usbd_transfer_stop(xfer) *------------------------------------------------------------------------*/ - "xfer" is a pointer to a USB transfer that should be stopped NOTE: this function must be called with "priv_mtx" locked NOTE: if the transfer was in progress, the callback will called with "xfer->error=USBD_CANCELLED", before this function returns /*------------------------------------------------------------------------* * struct usbd_config { * type, endpoint, direction, interval, timeout, frames, index * flags, bufsize, callback * }; *------------------------------------------------------------------------*/ - The "type" field selects the USB pipe type. Valid values are: UE_INTERRUPT, UE_CONTROL, UE_BULK, UE_ISOCHRONOUS. The special value UE_BULK_INTR will select BULK and INTERRUPT pipes. This field is mandatory. - The "endpoint" field selects the USB endpoint number. A value of 0xFF, "-1" or "UE_ADDR_ANY" will select the first matching endpoint. This field is mandatory. - The "direction" field selects the USB endpoint direction. A value of 0xFF, "-1" or "UE_DIR_ANY" will select the first matching endpoint. Else valid values are: "UE_DIR_IN" and "UE_DIR_OUT". This field is mandatory. - The "interval" field selects the interrupt interval, for "type" = UE_INTERRUPT. The "interval" is given in milliseconds. "0" selects the default interrupt interval. - The "timeout" field, if non-zero, will set the transfer timeout, in milliseconds. - The "frames" field sets the number of isochronous frames, for "type" = UE_ISOCHRONOUS. - The "index" field allows one to give a number, in case more endpoints match the description, that selects which matching "index" should be used. - The "flags" field allows one to set flags for the transfer. Valid flags are: USBD_SYNCHRONOUS This flag can only be used with the default callback, "usbd_default_callback()", and will cause the "usbd_transfer_start()" function to sleep, exiting all mutexes, until the transfer is finished. This flag is depreciated. USBD_FORCE_SHORT_XFER This flag forces the last USB packet sent to be short. A short packet has a length of less than "xfer->max_packet_size", which derives from "wMaxPacketSize". USBD_SHORT_XFER_OK This flag allows the transfer length, "xfer->actlen" to be less than "xfer->length", upon completion of a transfer. USBD_CUSTOM_CLEARSTALL USBD_USE_POLLING This flag can be used with any callback and will cause the "usbd_transfer_start()" function to wait, using "DELAY()", without exiting any mutexes, until the transfer is finished or has timed out. USBD_USE_DMA This flag will cause the USB host controller driver to not allocate a second data buffer, "xfer->buffer". Instead of transferring data using "xfer->buffer", data must be transferred by a call to "usbd_copy_in(&(xfer->buf_data), offset, src, len)" or "usbd_copy_out(&(xfer->buf_data), offset, dst, len)". This saves an extra data copy. - The "bufsize" field sets the total buffer size in bytes. If this field is zero, "wMaxPacketSize" will be used, multiplied by the "frames" field if the transfer type is isochronous. This is useful for setting up interrupt pipes. This field is mandatory. NOTE: For control transfers "bufsize" includes the length of the request structure. - The "callback" field sets the USB callback. This field is mandatory. MUTEX NOTE: =========== When you create a mutex, using "mtx_init()", don't forget to call "mtx_destroy()" at detach, else you can get "freed memory accessed" panics. --HPS ADAPTING OLD DRIVERS The traditional FreeBSD USB drivers use a number of macros to adapt the code to the various *BSD versions. Additionally, some of the callbacks differ. Below are some notes on adapting the drivers ------------------------------------------- #define FOOUNIT(dev) (minor(dev)) this is customary to get the unit number from a device_t USB_DECLARE_DRIVER(foo); --> This macro is used to declare the prototypes for the various functions used in the driver. Can be replaced by static device_probe_t foo_match; static device_attach_t foo_attach; static device_detach_t foo_detach; static devclass_t foo_devclass; static device_method_t foo_methods[] = { DEVMETHOD(device_probe, foo_match), /* or foo_probe if you like */ DEVMETHOD(device_attach, foo_attach), DEVMETHOD(device_detach, foo_detach), {0,0}, /* init */ {0,0} /* terminator if not supplied before */ }; static driver_t foo_driver = { "foo", foo_methods, sizeof(struct foo_softc) }; MODULE_DEPEND(foo, usb, 1, 1, 1); USB_MATCH(foo) { USB_MATCH_START(foo, uaa); --> These macros can be replaced by static int foo_match(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); USB_ATTACH(foo) { USB_ATTACH_START(foo, sc, uaa); --> can be replaced by static int pwc_attach(device_t dev) { struct pwc_softc *sc = device_get_softc(dev); struct usb_attach_arg *uaa = device_get_ivars(dev); USB_DETACH(ulpt) { USB_DETACH_START(ulpt, sc); --> can be replaced by static int pwc_detach(device_t dev) { struct pwc_softc *sc = device_get_softc(dev); int foo_open(struct cdev *dev, int flag, int mode, usb_proc_ptr p) { struct foo_softc *sc; USB_GET_SC_OPEN(foo, FOOUNIT(dev), sc); --> replace with int foo_open(struct cdev *dev, int flag, int mode, usb_proc_ptr p) { int unit = FOOUNIT(dev); struct foo_softc *sc = devclass_get_softc(foo_devclass, unit); if (sc == NULL) return ENXIO; ... USB_GET_SC(foo, FOOUNIT(dev), sc); --> this is used within the handlers(read, write etc) as a replacement for sc = devclass_get_softc(foo_devclass, FOOUNIT(dev)); pwcbsd/usb/ehci_pci.c000644 000423 000000 00000031152 10551670753 015311 0ustar00luigiwheel000000 000000 /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (augustss@carlstedt.se) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD: src/sys/dev/usb/ehci_pci.c,v 1.23 2006/09/07 00:06:41 imp Exp $"); /* * USB Enhanced Host Controller Driver, a.k.a. USB 2.0 controller. * * The EHCI 1.0 spec can be found at * http://developer.intel.com/technology/usb/download/ehci-r10.pdf * and the USB 2.0 spec at * http://www.usb.org/developers/docs/usb_20.zip */ /* The low level controller code for EHCI has been split into * PCI probes and EHCI specific code. This was done to facilitate the * sharing of code between *BSD's */ #include "opt_bus.h" #include #include #include #include #include /* LIST_XXX() */ #include #include #define INCLUDE_PCIXXX_H #include #include #include #include #define PCI_EHCI_VENDORID_ACERLABS 0x10b9 #define PCI_EHCI_VENDORID_AMD 0x1022 #define PCI_EHCI_VENDORID_APPLE 0x106b #define PCI_EHCI_VENDORID_ATI 0x1002 #define PCI_EHCI_VENDORID_CMDTECH 0x1095 #define PCI_EHCI_VENDORID_INTEL 0x8086 #define PCI_EHCI_VENDORID_NEC 0x1033 #define PCI_EHCI_VENDORID_OPTI 0x1045 #define PCI_EHCI_VENDORID_PHILIPS 0x1131 #define PCI_EHCI_VENDORID_SIS 0x1039 #define PCI_EHCI_VENDORID_NVIDIA 0x12D2 #define PCI_EHCI_VENDORID_NVIDIA2 0x10DE #define PCI_EHCI_VENDORID_VIA 0x1106 #define PCI_EHCI_BASE_REG 0x10 static void ehci_pci_givecontroller(device_t self); static void ehci_pci_takecontroller(device_t self); static int ehci_pci_suspend(device_t self) { ehci_softc_t *sc = device_get_softc(self); int err; err = bus_generic_suspend(self); if (err) return (err); ehci_suspend(sc); return 0; } static int ehci_pci_resume(device_t self) { ehci_softc_t *sc = device_get_softc(self); ehci_pci_takecontroller(self); ehci_resume(sc); bus_generic_resume(self); return 0; } static int ehci_pci_shutdown(device_t self) { ehci_softc_t *sc = device_get_softc(self); int err; err = bus_generic_shutdown(self); if (err) return (err); ehci_shutdown(sc); ehci_pci_givecontroller(self); return 0; } static const char * ehci_pci_match(device_t self) { u_int32_t device_id = pci_get_devid(self); if (device_id == 0x523910b9) return "ALi M5239 USB 2.0 controller"; if (device_id == 0x10227463) return "AMD 8111 USB 2.0 controller"; if (device_id == 0x43451002) return "ATI SB200 USB 2.0 controller"; if (device_id == 0x43731002) return "ATI SB400 USB 2.0 controller"; if (device_id == 0x25ad8086) return "Intel 6300ESB USB 2.0 controller"; if (device_id == 0x24cd8086) return "Intel 82801DB/L/M (ICH4) USB 2.0 controller"; if (device_id == 0x24dd8086) return "Intel 82801EB/R (ICH5) USB 2.0 controller"; if (device_id == 0x265c8086) return "Intel 82801FB (ICH6) USB 2.0 controller"; if (device_id == 0x27cc8086) return "Intel 82801GB/R (ICH7) USB 2.0 controller"; if(device_id == 0x00e01033) { return ("NEC uPD 720100 USB 2.0 controller"); } if (device_id == 0x006810de) return "NVIDIA nForce2 USB 2.0 controller"; if (device_id == 0x008810de) return "NVIDIA nForce2 Ultra 400 USB 2.0 controller"; if (device_id == 0x00d810de) return "NVIDIA nForce3 USB 2.0 controller"; if (device_id == 0x00e810de) return "NVIDIA nForce3 250 USB 2.0 controller"; if (device_id == 0x005b10de) return "NVIDIA nForce4 USB 2.0 controller"; if (device_id == 0x15621131) return "Philips ISP156x USB 2.0 controller"; if(device_id == 0x31041106) { return ("VIA VT6202 USB 2.0 controller"); } if((pci_get_class(self) == PCIC_SERIALBUS) && (pci_get_subclass(self) == PCIS_SERIALBUS_USB) && (pci_get_progif(self) == PCI_INTERFACE_EHCI)) { return ("EHCI (generic) USB 2.0 controller"); } return NULL; /* dunno */ } static int ehci_pci_probe(device_t self) { const char *desc = ehci_pci_match(self); if (desc) { device_set_desc(self, desc); return 0; } else { return ENXIO; } } static int ehci_pci_detach(device_t self); static int ehci_pci_attach(device_t self) { ehci_softc_t *sc = device_get_softc(self); int err; int rid; sc = usbd_mem_alloc(device_get_dma_tag(self), sizeof(*sc), LOG2(EHCI_FRAMELIST_ALIGN)); if(sc == NULL) { device_printf(self, "Could not allocate sc\n"); return ENXIO; } #if 1 bzero(sc, sizeof(*sc)); #endif sc->sc_physaddr = usbd_mem_vtophys(sc, sizeof(*sc)); /* physical address of sc */ mtx_init(&sc->sc_bus.mtx, "usb lock", NULL, MTX_DEF|MTX_RECURSE); device_set_softc(self, sc); sc->sc_dev = self; pci_enable_busmaster(self); switch(pci_read_config(self, PCI_USBREV, 1) & PCI_USBREV_MASK) { case PCI_USBREV_PRE_1_0: case PCI_USBREV_1_0: case PCI_USBREV_1_1: sc->sc_bus.usbrev = USBREV_UNKNOWN; printf("pre-2.0 USB rev\n"); return ENXIO; case PCI_USBREV_2_0: sc->sc_bus.usbrev = USBREV_2_0; break; default: sc->sc_bus.usbrev = USBREV_UNKNOWN; break; } sc->sc_bus.dma_tag = usbd_dma_tag_alloc(device_get_dma_tag(self), USB_PAGE_SIZE, USB_PAGE_SIZE); if (sc->sc_bus.dma_tag == NULL) { device_printf(self, "Could not allocate DMA tag\n"); goto error; } rid = PCI_CBMEM; sc->io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid, RF_ACTIVE); if(!sc->io_res) { device_printf(self, "Could not map memory\n"); goto error; } sc->iot = rman_get_bustag(sc->io_res); sc->ioh = rman_get_bushandle(sc->io_res); rid = 0; sc->irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); if (sc->irq_res == NULL) { device_printf(self, "Could not allocate irq\n"); goto error; } sc->sc_bus.bdev = device_add_child(self, "usb", -1); if(!sc->sc_bus.bdev) { device_printf(self, "Could not add USB device\n"); goto error; } device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); device_set_softc(sc->sc_bus.bdev, &sc->sc_bus); /* ehci_pci_match will never return NULL if ehci_pci_probe succeeded */ device_set_desc(sc->sc_bus.bdev, ehci_pci_match(self)); switch (pci_get_vendor(self)) { case PCI_EHCI_VENDORID_ACERLABS: sprintf(sc->sc_vendor, "AcerLabs"); break; case PCI_EHCI_VENDORID_AMD: sprintf(sc->sc_vendor, "AMD"); break; case PCI_EHCI_VENDORID_APPLE: sprintf(sc->sc_vendor, "Apple"); break; case PCI_EHCI_VENDORID_ATI: sprintf(sc->sc_vendor, "ATI"); break; case PCI_EHCI_VENDORID_CMDTECH: sprintf(sc->sc_vendor, "CMDTECH"); break; case PCI_EHCI_VENDORID_INTEL: sprintf(sc->sc_vendor, "Intel"); break; case PCI_EHCI_VENDORID_NEC: sprintf(sc->sc_vendor, "NEC"); break; case PCI_EHCI_VENDORID_OPTI: sprintf(sc->sc_vendor, "OPTi"); break; case PCI_EHCI_VENDORID_PHILIPS: sprintf(sc->sc_vendor, "Philips"); break; case PCI_EHCI_VENDORID_SIS: sprintf(sc->sc_vendor, "SiS"); break; case PCI_EHCI_VENDORID_NVIDIA: case PCI_EHCI_VENDORID_NVIDIA2: sprintf(sc->sc_vendor, "nVidia"); break; case PCI_EHCI_VENDORID_VIA: sprintf(sc->sc_vendor, "VIA"); break; default: if (bootverbose) device_printf(self, "(New EHCI DeviceId=0x%08x)\n", pci_get_devid(self)); sprintf(sc->sc_vendor, "(0x%04x)", pci_get_vendor(self)); } err = bus_setup_intr(self, sc->irq_res, INTR_TYPE_BIO|INTR_MPSAFE, (void *)(void *)ehci_interrupt, sc, &sc->ih); if(err) { device_printf(self, "Could not setup irq, %d\n", err); sc->ih = NULL; goto error; } ehci_pci_takecontroller(self); err = ehci_init(sc); if (!err) { err = device_probe_and_attach(sc->sc_bus.bdev); } if (err) { device_printf(self, "USB init failed err=%d\n", err); goto error; } return 0; error: ehci_pci_detach(self); return ENXIO; } static int ehci_pci_detach(device_t self) { ehci_softc_t *sc = device_get_softc(self); if(sc->sc_bus.bdev) { device_delete_child(self, sc->sc_bus.bdev); sc->sc_bus.bdev = NULL; } pci_disable_busmaster(self); /* * disable interrupts that might have been switched on in ehci_init */ if(sc->io_res) { bus_space_write_4(sc->iot, sc->ioh, EHCI_USBINTR, 0); } if(sc->irq_res && sc->ih) { /* only call ehci_detach() * after ehci_init() */ ehci_detach(sc); int err = bus_teardown_intr(self, sc->irq_res, sc->ih); if (err) /* XXX or should we panic? */ device_printf(self, "Could not tear down irq, %d\n", err); sc->ih = NULL; } if (sc->irq_res) { bus_release_resource(self, SYS_RES_IRQ, 0, sc->irq_res); sc->irq_res = NULL; } if (sc->io_res) { bus_release_resource(self, SYS_RES_MEMORY, PCI_CBMEM, sc->io_res); sc->io_res = NULL; } if (sc->sc_bus.dma_tag) { usbd_dma_tag_free(sc->sc_bus.dma_tag); } mtx_destroy(&sc->sc_bus.mtx); usbd_mem_free(sc, sizeof(*sc)); device_set_softc(self, NULL); return 0; } static void ehci_pci_takecontroller(device_t self) { ehci_softc_t *sc = device_get_softc(self); u_int32_t cparams, eec, legsup; int eecp, i; cparams = EREAD4(sc, EHCI_HCCPARAMS); /* Synchronise with the BIOS if it owns the controller. */ for (eecp = EHCI_HCC_EECP(cparams); eecp != 0; eecp = EHCI_EECP_NEXT(eec)) { eec = pci_read_config(self, eecp, 4); if(EHCI_EECP_ID(eec) != EHCI_EC_LEGSUP) { continue; } legsup = eec; pci_write_config(self, eecp, legsup | EHCI_LEGSUP_OSOWNED, 4); if (legsup & EHCI_LEGSUP_BIOSOWNED) { device_printf(sc->sc_bus.bdev, "waiting for BIOS " "to give up control\n"); for (i = 0; i < 5000; i++) { legsup = pci_read_config(self, eecp, 4); if((legsup & EHCI_LEGSUP_BIOSOWNED) == 0) { break; } DELAY(1000); } if(legsup & EHCI_LEGSUP_BIOSOWNED) { device_printf(sc->sc_bus.bdev, "timed out waiting for BIOS\n"); } } } } static void ehci_pci_givecontroller(device_t self) { ehci_softc_t *sc = device_get_softc(self); u_int32_t cparams, eec, legsup; int eecp; cparams = EREAD4(sc, EHCI_HCCPARAMS); for (eecp = EHCI_HCC_EECP(cparams); eecp != 0; eecp = EHCI_EECP_NEXT(eec)) { eec = pci_read_config(self, eecp, 4); if(EHCI_EECP_ID(eec) != EHCI_EC_LEGSUP) { continue; } legsup = eec; pci_write_config(self, eecp, legsup & ~EHCI_LEGSUP_OSOWNED, 4); } } static driver_t ehci_driver = { .name = "ehci", .methods = (device_method_t []) { /* device interface */ DEVMETHOD(device_probe, ehci_pci_probe), DEVMETHOD(device_attach, ehci_pci_attach), DEVMETHOD(device_detach, ehci_pci_detach), DEVMETHOD(device_suspend, ehci_pci_suspend), DEVMETHOD(device_resume, ehci_pci_resume), DEVMETHOD(device_shutdown, ehci_pci_shutdown), /* bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), {0, 0} }, .size = 0, }; static devclass_t ehci_devclass; DRIVER_MODULE(ehci, pci, ehci_driver, ehci_devclass, 0, 0); DRIVER_MODULE(ehci, cardbus, ehci_driver, ehci_devclass, 0, 0); pwcbsd/usb/ehci.c000644 000423 000000 00000252461 10551670753 014466 0ustar00luigiwheel000000 000000 /*- * Copyright (c) 2004 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) and by Charles M. Hannum. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * USB Enhanced Host Controller Driver, a.k.a. USB 2.0 controller. * * The EHCI 0.96 spec can be found at * http://developer.intel.com/technology/usb/download/ehci-r096.pdf * The EHCI 1.0 spec can be found at * http://developer.intel.com/technology/usb/download/ehci-r10.pdf * and the USB 2.0 spec at * http://www.usb.org/developers/docs/usb_20.zip * */ /* * TODO: * 1) command failures are not recovered correctly */ #include __FBSDID("$FreeBSD: src/sys/dev/usb/ehci.c,v 1.49 2006/09/07 00:06:41 imp Exp $"); #include #include #include #include #include /* LIST_XXX() */ #include #include #define INCLUDE_PCIXXX_H #include #include #include #include #define MS_TO_TICKS(ms) (((ms) * hz) / 1000) #define EHCI_BUS2SC(bus) ((ehci_softc_t *)(((u_int8_t *)(bus)) - \ POINTER_TO_UNSIGNED(&(((ehci_softc_t *)0)->sc_bus)))) #ifdef USB_DEBUG #define DPRINTF(x) { if (ehcidebug) { printf("%s: ", __FUNCTION__); printf x ; } } #define DPRINTFN(n,x) { if (ehcidebug > (n)) { printf("%s: ", __FUNCTION__); printf x ; } } int ehcidebug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, ehci, CTLFLAG_RW, 0, "USB ehci"); SYSCTL_INT(_hw_usb_ehci, OID_AUTO, debug, CTLFLAG_RW, &ehcidebug, 0, "ehci debug level"); static void ehci_dump_regs(ehci_softc_t *sc); static void ehci_dump_sqh(ehci_qh_t *sqh); #else #define DPRINTF(x) #define DPRINTFN(n,x) #endif #define EHCI_INTR_ENDPT 1 extern struct usbd_bus_methods ehci_bus_methods; extern struct usbd_pipe_methods ehci_device_bulk_methods; extern struct usbd_pipe_methods ehci_device_ctrl_methods; extern struct usbd_pipe_methods ehci_device_intr_methods; extern struct usbd_pipe_methods ehci_device_isoc_fs_methods; extern struct usbd_pipe_methods ehci_device_isoc_hs_methods; extern struct usbd_pipe_methods ehci_root_ctrl_methods; extern struct usbd_pipe_methods ehci_root_intr_methods; #define PHYSADDR(sc,what) \ ((sc)->sc_physaddr + POINTER_TO_UNSIGNED(&(((struct ehci_softc *)0)->what))) usbd_status ehci_init(ehci_softc_t *sc) { u_int32_t version, sparams, cparams, hcr; u_int i; u_int16_t x; u_int16_t y; u_int16_t bit; usbd_status err = 0; mtx_lock(&sc->sc_bus.mtx); DPRINTF(("start\n")); LIST_INIT(&sc->sc_interrupt_list_head); __callout_init_mtx(&sc->sc_tmo_pcd, &sc->sc_bus.mtx, CALLOUT_RETURNUNLOCKED); #ifdef USB_DEBUG if(ehcidebug > 2) { ehci_dump_regs(sc); } #endif sc->sc_offs = EREAD1(sc, EHCI_CAPLENGTH); version = EREAD2(sc, EHCI_HCIVERSION); device_printf(sc->sc_bus.bdev, "EHCI version %x.%x\n", version >> 8, version & 0xff); sparams = EREAD4(sc, EHCI_HCSPARAMS); DPRINTF(("sparams=0x%x\n", sparams)); sc->sc_noport = EHCI_HCS_N_PORTS(sparams); cparams = EREAD4(sc, EHCI_HCCPARAMS); DPRINTF(("cparams=0x%x\n", cparams)); if(EHCI_HCC_64BIT(cparams)) { DPRINTF(("HCC uses 64-bit structures\n")); /* MUST clear segment register if 64 bit capable */ EWRITE4(sc, EHCI_CTRLDSSEGMENT, 0); } sc->sc_bus.usbrev = USBREV_2_0; /* Reset the controller */ DPRINTF(("%s: resetting\n", device_get_nameunit(sc->sc_bus.bdev))); EOWRITE4(sc, EHCI_USBCMD, 0); /* Halt controller */ DELAY(1000*1); EOWRITE4(sc, EHCI_USBCMD, EHCI_CMD_HCRESET); for(i = 0; i < 100; i++) { DELAY(1000*1); hcr = EOREAD4(sc, EHCI_USBCMD) & EHCI_CMD_HCRESET; if(!hcr) { break; } } if (hcr) { device_printf(sc->sc_bus.bdev, "reset timeout\n"); err = USBD_IOERROR; goto done; } /* use current frame-list-size selection * 0: 1024*4 bytes * 1: 512*4 bytes * 2: 256*4 bytes * 3: unknown */ if(EHCI_CMD_FLS(EOREAD4(sc, EHCI_USBCMD)) == 3) { device_printf(sc->sc_bus.bdev, "invalid frame-list-size\n"); err = USBD_IOERROR; goto done; } /* set up the bus struct */ sc->sc_bus.methods = &ehci_bus_methods; sc->sc_eintrs = EHCI_NORMAL_INTRS; for(i = 0; i < EHCI_VIRTUAL_FRAMELIST_COUNT; i++) { sc->sc_hw.intr_start[i].qh_self = htole32(PHYSADDR(sc,sc_hw.intr_start[i])|EHCI_LINK_QH); sc->sc_hw.intr_start[i].qh_endp = htole32(EHCI_QH_SET_EPS(EHCI_QH_SPEED_HIGH)); sc->sc_hw.intr_start[i].qh_endphub = htole32(EHCI_QH_SET_MULT(1)); sc->sc_hw.intr_start[i].qh_curqtd = 0; sc->sc_hw.intr_start[i].qh_qtd.qtd_next = htole32(EHCI_LINK_TERMINATE); sc->sc_hw.intr_start[i].qh_qtd.qtd_altnext = htole32(EHCI_LINK_TERMINATE); sc->sc_hw.intr_start[i].qh_qtd.qtd_status = htole32(EHCI_QTD_HALTED); sc->sc_intr_p_last[i] = &sc->sc_hw.intr_start[i]; } /* * the QHs are arranged to give poll intervals that are * powers of 2 times 1ms */ bit = EHCI_VIRTUAL_FRAMELIST_COUNT/2; while(bit) { x = bit; while(x & bit) { y = (x ^ bit)|(bit/2); /* the next QH has half the * poll interval */ sc->sc_hw.intr_start[x].qh_link = sc->sc_hw.intr_start[y].qh_self; x++; } bit >>= 1; } /* the last (1ms) QH terminates */ sc->sc_hw.intr_start[0].qh_link = htole32(EHCI_LINK_TERMINATE); for(i = 0; i < EHCI_VIRTUAL_FRAMELIST_COUNT; i++) { /* initialize full speed isochronous */ sc->sc_hw.isoc_fs_start[i].sitd_self = htole32(PHYSADDR(sc,sc_hw.isoc_fs_start[i])|EHCI_LINK_SITD); sc->sc_hw.isoc_fs_start[i].sitd_back = htole32(EHCI_LINK_TERMINATE); sc->sc_hw.isoc_fs_start[i].sitd_next = sc->sc_hw.intr_start[i|(EHCI_VIRTUAL_FRAMELIST_COUNT/2)].qh_self; sc->sc_isoc_fs_p_last[i] = &sc->sc_hw.isoc_fs_start[i]; /* initialize high speed isochronous */ sc->sc_hw.isoc_hs_start[i].itd_self = htole32(PHYSADDR(sc,sc_hw.isoc_hs_start[i])|EHCI_LINK_ITD); sc->sc_hw.isoc_hs_start[i].itd_next = sc->sc_hw.isoc_fs_start[i].sitd_self; sc->sc_isoc_hs_p_last[i] = &sc->sc_hw.isoc_hs_start[i]; } /* * execution order: * pframes -> high speed isochronous -> * full speed isochronous -> interrupt QH's */ for(i = 0; i < EHCI_FRAMELIST_COUNT; i++) { sc->sc_hw.pframes[i] = sc->sc_hw.isoc_hs_start [i & (EHCI_VIRTUAL_FRAMELIST_COUNT-1)].itd_self; } /* setup sync list pointer */ EOWRITE4(sc, EHCI_PERIODICLISTBASE, PHYSADDR(sc,sc_hw.pframes[0])); /* init dummy QH that starts the async list */ sc->sc_hw.async_start.qh_self = htole32(PHYSADDR(sc,sc_hw.async_start)|EHCI_LINK_QH); /* fill the QH */ sc->sc_hw.async_start.qh_endp = htole32(EHCI_QH_SET_EPS(EHCI_QH_SPEED_HIGH) | EHCI_QH_HRECL); sc->sc_hw.async_start.qh_endphub = htole32(EHCI_QH_SET_MULT(1)); sc->sc_hw.async_start.qh_link = sc->sc_hw.async_start.qh_self; sc->sc_hw.async_start.qh_curqtd = 0; /* fill the overlay qTD */ sc->sc_hw.async_start.qh_qtd.qtd_next = htole32(EHCI_LINK_TERMINATE); sc->sc_hw.async_start.qh_qtd.qtd_altnext = htole32(EHCI_LINK_TERMINATE); sc->sc_hw.async_start.qh_qtd.qtd_status = htole32(EHCI_QTD_HALTED); sc->sc_async_p_last = &sc->sc_hw.async_start; #ifdef USB_DEBUG if(ehcidebug) { ehci_dump_sqh(&sc->sc_hw.async_start); } #endif /* setup async list pointer */ EOWRITE4(sc, EHCI_ASYNCLISTADDR, PHYSADDR(sc,sc_hw.async_start)|EHCI_LINK_QH); /* enable interrupts */ EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs); /* turn on controller */ EOWRITE4(sc, EHCI_USBCMD, EHCI_CMD_ITC_2 | /* 2 microframes interrupt delay */ (EOREAD4(sc, EHCI_USBCMD) & EHCI_CMD_FLS_M) | EHCI_CMD_ASE | EHCI_CMD_PSE | EHCI_CMD_RS); /* Take over port ownership */ EOWRITE4(sc, EHCI_CONFIGFLAG, EHCI_CONF_CF); for(i = 0; i < 100; i++) { DELAY(1000*1); hcr = EOREAD4(sc, EHCI_USBSTS) & EHCI_STS_HCH; if(!hcr) { break; } } if (hcr) { device_printf(sc->sc_bus.bdev, "run timeout\n"); err = USBD_IOERROR; goto done; } done: mtx_unlock(&sc->sc_bus.mtx); return (err); } /* * shut down the controller when the system is going down */ void ehci_detach(struct ehci_softc *sc) { mtx_lock(&sc->sc_bus.mtx); __callout_stop(&sc->sc_tmo_pcd); EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs); EOWRITE4(sc, EHCI_USBCMD, 0); EOWRITE4(sc, EHCI_USBCMD, EHCI_CMD_HCRESET); DELAY(1000*300); /* XXX let stray task complete */ mtx_unlock(&sc->sc_bus.mtx); __callout_drain(&(sc->sc_tmo_pcd)); return; } void ehci_suspend(struct ehci_softc *sc) { u_int32_t cmd; u_int32_t hcr; u_int8_t i; mtx_lock(&sc->sc_bus.mtx); for(i = 1; i <= sc->sc_noport; i++) { cmd = EOREAD4(sc, EHCI_PORTSC(i)); if(((cmd & EHCI_PS_PO) == 0) && ((cmd & EHCI_PS_PE) == EHCI_PS_PE)) { EOWRITE4(sc, EHCI_PORTSC(i), cmd | EHCI_PS_SUSP); } } sc->sc_cmd = EOREAD4(sc, EHCI_USBCMD); cmd = sc->sc_cmd & ~(EHCI_CMD_ASE | EHCI_CMD_PSE); EOWRITE4(sc, EHCI_USBCMD, cmd); for(i = 0; i < 100; i++) { hcr = EOREAD4(sc, EHCI_USBSTS) & (EHCI_STS_ASS | EHCI_STS_PSS); if(hcr == 0) { break; } DELAY(1000*1); } if(hcr != 0) { device_printf(sc->sc_bus.bdev, "reset timeout\n"); } cmd &= ~EHCI_CMD_RS; EOWRITE4(sc, EHCI_USBCMD, cmd); for (i = 0; i < 100; i++) { hcr = EOREAD4(sc, EHCI_USBSTS) & EHCI_STS_HCH; if (hcr == EHCI_STS_HCH) { break; } DELAY(1000*1); } if (hcr != EHCI_STS_HCH) { device_printf(sc->sc_bus.bdev, "config timeout\n"); } mtx_unlock(&sc->sc_bus.mtx); return; } void ehci_resume(struct ehci_softc *sc) { u_int32_t cmd; u_int32_t hcr; u_int8_t i; mtx_lock(&sc->sc_bus.mtx); /* restore things in case the bios doesn't */ EOWRITE4(sc, EHCI_CTRLDSSEGMENT, 0); EOWRITE4(sc, EHCI_PERIODICLISTBASE, PHYSADDR(sc,sc_hw.pframes[0])); EOWRITE4(sc, EHCI_ASYNCLISTADDR, PHYSADDR(sc,sc_hw.async_start)|EHCI_LINK_QH); EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs); hcr = 0; for(i = 1; i <= sc->sc_noport; i++) { cmd = EOREAD4(sc, EHCI_PORTSC(i)); if(((cmd & EHCI_PS_PO) == 0) && ((cmd & EHCI_PS_SUSP) == EHCI_PS_SUSP)) { EOWRITE4(sc, EHCI_PORTSC(i), cmd | EHCI_PS_FPR); hcr = 1; } } if(hcr) { DELAY(1000*USB_RESUME_WAIT); for(i = 1; i <= sc->sc_noport; i++) { cmd = EOREAD4(sc, EHCI_PORTSC(i)); if(((cmd & EHCI_PS_PO) == 0) && ((cmd & EHCI_PS_SUSP) == EHCI_PS_SUSP)) { EOWRITE4(sc, EHCI_PORTSC(i), cmd & ~EHCI_PS_FPR); } } } EOWRITE4(sc, EHCI_USBCMD, sc->sc_cmd); for(i = 0; i < 100; i++) { hcr = EOREAD4(sc, EHCI_USBSTS) & EHCI_STS_HCH; if(hcr != EHCI_STS_HCH) { break; } DELAY(1000*1); } if(hcr == EHCI_STS_HCH) { device_printf(sc->sc_bus.bdev, "config timeout\n"); } mtx_unlock(&sc->sc_bus.mtx); DELAY(1000*USB_RESUME_WAIT); return; } void ehci_shutdown(ehci_softc_t *sc) { DPRINTF(("stopping the HC\n")); mtx_lock(&sc->sc_bus.mtx); EOWRITE4(sc, EHCI_USBCMD, 0); /* Halt controller */ EOWRITE4(sc, EHCI_USBCMD, EHCI_CMD_HCRESET); mtx_unlock(&sc->sc_bus.mtx); } #ifdef USB_DEBUG static void ehci_dump_regs(ehci_softc_t *sc) { u_int32_t i; i = EOREAD4(sc, EHCI_USBCMD); printf("cmd=0x%08x\n", i); if(i & EHCI_CMD_ITC_1) printf(" EHCI_CMD_ITC_1\n"); if(i & EHCI_CMD_ITC_2) printf(" EHCI_CMD_ITC_2\n"); if(i & EHCI_CMD_ITC_4) printf(" EHCI_CMD_ITC_4\n"); if(i & EHCI_CMD_ITC_8) printf(" EHCI_CMD_ITC_8\n"); if(i & EHCI_CMD_ITC_16) printf(" EHCI_CMD_ITC_16\n"); if(i & EHCI_CMD_ITC_32) printf(" EHCI_CMD_ITC_32\n"); if(i & EHCI_CMD_ITC_64) printf(" EHCI_CMD_ITC_64\n"); if(i & EHCI_CMD_ASPME) printf(" EHCI_CMD_ASPME\n"); if(i & EHCI_CMD_ASPMC) printf(" EHCI_CMD_ASPMC\n"); if(i & EHCI_CMD_LHCR) printf(" EHCI_CMD_LHCR\n"); if(i & EHCI_CMD_IAAD) printf(" EHCI_CMD_IAAD\n"); if(i & EHCI_CMD_ASE) printf(" EHCI_CMD_ASE\n"); if(i & EHCI_CMD_PSE) printf(" EHCI_CMD_PSE\n"); if(i & EHCI_CMD_FLS_M) printf(" EHCI_CMD_FLS_M\n"); if(i & EHCI_CMD_HCRESET) printf(" EHCI_CMD_HCRESET\n"); if(i & EHCI_CMD_RS) printf(" EHCI_CMD_RS\n"); i = EOREAD4(sc, EHCI_USBSTS); printf("sts=0x%08x\n", i); if(i & EHCI_STS_ASS) printf(" EHCI_STS_ASS\n"); if(i & EHCI_STS_PSS) printf(" EHCI_STS_PSS\n"); if(i & EHCI_STS_REC) printf(" EHCI_STS_REC\n"); if(i & EHCI_STS_HCH) printf(" EHCI_STS_HCH\n"); if(i & EHCI_STS_IAA) printf(" EHCI_STS_IAA\n"); if(i & EHCI_STS_HSE) printf(" EHCI_STS_HSE\n"); if(i & EHCI_STS_FLR) printf(" EHCI_STS_FLR\n"); if(i & EHCI_STS_PCD) printf(" EHCI_STS_PCD\n"); if(i & EHCI_STS_ERRINT) printf(" EHCI_STS_ERRINT\n"); if(i & EHCI_STS_INT) printf(" EHCI_STS_INT\n"); printf("ien=0x%08x\n", EOREAD4(sc, EHCI_USBINTR)); printf("frindex=0x%08x ctrdsegm=0x%08x periodic=0x%08x async=0x%08x\n", EOREAD4(sc, EHCI_FRINDEX), EOREAD4(sc, EHCI_CTRLDSSEGMENT), EOREAD4(sc, EHCI_PERIODICLISTBASE), EOREAD4(sc, EHCI_ASYNCLISTADDR)); for(i = 1; i <= sc->sc_noport; i++) { printf("port %d status=0x%08x\n", i, EOREAD4(sc, EHCI_PORTSC(i))); } return; } static void ehci_dump_link(u_int32_t link, int type) { link = le32toh(link); printf("0x%08x", link); if (link & EHCI_LINK_TERMINATE) printf(""); else { printf("<"); if (type) { switch (EHCI_LINK_TYPE(link)) { case EHCI_LINK_ITD: printf("ITD"); break; case EHCI_LINK_QH: printf("QH"); break; case EHCI_LINK_SITD: printf("SITD"); break; case EHCI_LINK_FSTN: printf("FSTN"); break; } } printf(">"); } return; } static void ehci_dump_qtd(ehci_qtd_t *qtd) { u_int32_t s; printf(" next="); ehci_dump_link(qtd->qtd_next, 0); printf(" altnext="); ehci_dump_link(qtd->qtd_altnext, 0); printf("\n"); s = le32toh(qtd->qtd_status); printf(" status=0x%08x: toggle=%d bytes=0x%x ioc=%d c_page=0x%x\n", s, EHCI_QTD_GET_TOGGLE(s), EHCI_QTD_GET_BYTES(s), EHCI_QTD_GET_IOC(s), EHCI_QTD_GET_C_PAGE(s)); printf(" cerr=%d pid=%d stat=%s%s%s%s%s%s%s%s\n", EHCI_QTD_GET_CERR(s), EHCI_QTD_GET_PID(s), (s & EHCI_QTD_ACTIVE) ? "ACTIVE" : "NOT_ACTIVE", (s & EHCI_QTD_HALTED) ? "-HALTED" : "", (s & EHCI_QTD_BUFERR) ? "-BUFERR" : "", (s & EHCI_QTD_BABBLE) ? "-BABBLE" : "", (s & EHCI_QTD_XACTERR) ? "-XACTERR" : "", (s & EHCI_QTD_MISSEDMICRO) ? "-MISSED" : "", (s & EHCI_QTD_SPLITXSTATE) ? "-SPLIT" : "", (s & EHCI_QTD_PINGSTATE) ? "-PING" : ""); for(s = 0; s < 5; s++) { printf(" buffer[%d]=0x%08x\n", s, le32toh(qtd->qtd_buffer[s])); } for(s = 0; s < 5; s++) { printf(" buffer_hi[%d]=0x%08x\n", s, le32toh(qtd->qtd_buffer_hi[s])); } return; } static void ehci_dump_sqtd(ehci_qtd_t *sqtd) { printf("QTD(%p) at 0x%08x:\n", sqtd, le32toh(sqtd->qtd_self)); ehci_dump_qtd(sqtd); return; } static void ehci_dump_sqtds(ehci_qtd_t *sqtd) { u_int16_t i; u_int32_t stop; stop = 0; for(i = 0; sqtd && (i < 20) && !stop; sqtd = sqtd->obj_next, i++) { ehci_dump_sqtd(sqtd); stop = sqtd->qtd_next & htole32(EHCI_LINK_TERMINATE); } if(sqtd) { printf("dump aborted, too many TDs\n"); } return; } static void ehci_dump_sqh(ehci_qh_t *qh) { u_int32_t endp, endphub; printf("QH(%p) at 0x%08x:\n", qh, le32toh(qh->qh_self) & ~0x1F); printf(" link="); ehci_dump_link(qh->qh_link, 1); printf("\n"); endp = le32toh(qh->qh_endp); printf(" endp=0x%08x\n", endp); printf(" addr=0x%02x inact=%d endpt=%d eps=%d dtc=%d hrecl=%d\n", EHCI_QH_GET_ADDR(endp), EHCI_QH_GET_INACT(endp), EHCI_QH_GET_ENDPT(endp), EHCI_QH_GET_EPS(endp), EHCI_QH_GET_DTC(endp), EHCI_QH_GET_HRECL(endp)); printf(" mpl=0x%x ctl=%d nrl=%d\n", EHCI_QH_GET_MPL(endp), EHCI_QH_GET_CTL(endp), EHCI_QH_GET_NRL(endp)); endphub = le32toh(qh->qh_endphub); printf(" endphub=0x%08x\n", endphub); printf(" smask=0x%02x cmask=0x%02x huba=0x%02x port=%d mult=%d\n", EHCI_QH_GET_SMASK(endphub), EHCI_QH_GET_CMASK(endphub), EHCI_QH_GET_HUBA(endphub), EHCI_QH_GET_PORT(endphub), EHCI_QH_GET_MULT(endphub)); printf(" curqtd="); ehci_dump_link(qh->qh_curqtd, 0); printf("\n"); printf("Overlay qTD:\n"); ehci_dump_qtd((void *)&qh->qh_qtd); return; } static void ehci_dump_sitd(ehci_sitd_t *sitd) { printf("SITD(%p) at 0x%08x\n", sitd, le32toh(sitd->sitd_self) & ~0x1F); printf(" next=0x%08x\n", le32toh(sitd->sitd_next)); printf(" portaddr=0x%08x dir=%s addr=%d endpt=0x%x port=0x%x huba=0x%x\n", le32toh(sitd->sitd_portaddr), (sitd->sitd_portaddr & htole32(EHCI_SITD_SET_DIR_IN)) ? "in" : "out" , EHCI_SITD_GET_ADDR(le32toh(sitd->sitd_portaddr)), EHCI_SITD_GET_ENDPT(le32toh(sitd->sitd_portaddr)), EHCI_SITD_GET_PORT(le32toh(sitd->sitd_portaddr)), EHCI_SITD_GET_HUBA(le32toh(sitd->sitd_portaddr))); printf(" mask=0x%08x\n", le32toh(sitd->sitd_mask)); printf(" status=0x%08x <%s> len=0x%x\n", le32toh(sitd->sitd_status), (sitd->sitd_status & htole32(EHCI_SITD_ACTIVE)) ? "ACTIVE" : "", EHCI_SITD_GET_LEN(le32toh(sitd->sitd_status))); printf(" back=0x%08x, bp=0x%08x,0x%08x,0x%08x,0x%08x\n", le32toh(sitd->sitd_back), le32toh(sitd->sitd_bp[0]), le32toh(sitd->sitd_bp[1]), le32toh(sitd->sitd_bp_hi[0]), le32toh(sitd->sitd_bp_hi[1])); return; } static void ehci_dump_itd(ehci_itd_t *itd) { printf("ITD(%p) at 0x%08x\n", itd, le32toh(itd->itd_self) & ~0x1F); printf(" next=0x%08x\n", le32toh(itd->itd_next)); printf(" status[0]=0x%08x; <%s>\n", le32toh(itd->itd_status[0]), (itd->itd_status[0] & htole32(EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); printf(" status[1]=0x%08x; <%s>\n", le32toh(itd->itd_status[1]), (itd->itd_status[1] & htole32(EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); printf(" status[2]=0x%08x; <%s>\n", le32toh(itd->itd_status[2]), (itd->itd_status[2] & htole32(EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); printf(" status[3]=0x%08x; <%s>\n", le32toh(itd->itd_status[3]), (itd->itd_status[3] & htole32(EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); printf(" status[4]=0x%08x; <%s>\n", le32toh(itd->itd_status[4]), (itd->itd_status[4] & htole32(EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); printf(" status[5]=0x%08x; <%s>\n", le32toh(itd->itd_status[5]), (itd->itd_status[5] & htole32(EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); printf(" status[6]=0x%08x; <%s>\n", le32toh(itd->itd_status[6]), (itd->itd_status[6] & htole32(EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); printf(" status[7]=0x%08x; <%s>\n", le32toh(itd->itd_status[7]), (itd->itd_status[7] & htole32(EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); printf(" bp[0]=0x%08x\n", le32toh(itd->itd_bp[0])); printf(" addr=0x%02x; endpt=0x%01x\n", EHCI_ITD_GET_ADDR(le32toh(itd->itd_bp[0])), EHCI_ITD_GET_ENDPT(le32toh(itd->itd_bp[0]))); printf(" bp[1]=0x%08x\n", le32toh(itd->itd_bp[1])); printf(" dir=%s; mpl=0x%02x\n", (le32toh(itd->itd_bp[1]) & EHCI_ITD_SET_DIR_IN) ? "in" : "out", EHCI_ITD_GET_MPL(le32toh(itd->itd_bp[1]))); printf(" bp[2..6]=0x%08x,0x%08x,0x%08x,0x%08x,0x%08x\n", le32toh(itd->itd_bp[2]), le32toh(itd->itd_bp[3]), le32toh(itd->itd_bp[4]), le32toh(itd->itd_bp[5]), le32toh(itd->itd_bp[6])); printf(" bp_hi=0x%08x,0x%08x,0x%08x,0x%08x,\n" " 0x%08x,0x%08x,0x%08x\n", le32toh(itd->itd_bp_hi[0]), le32toh(itd->itd_bp_hi[1]), le32toh(itd->itd_bp_hi[2]), le32toh(itd->itd_bp_hi[3]), le32toh(itd->itd_bp_hi[4]), le32toh(itd->itd_bp_hi[5]), le32toh(itd->itd_bp_hi[6])); return; } static void ehci_dump_isoc(ehci_softc_t *sc) { ehci_itd_t *itd; ehci_sitd_t *sitd; u_int16_t max = 1000; u_int16_t pos; pos = (EOREAD4(sc, EHCI_FRINDEX) / 8) & (EHCI_VIRTUAL_FRAMELIST_COUNT-1); printf("%s: isochronous dump from frame 0x%03x:\n", __FUNCTION__, pos); itd = sc->sc_isoc_hs_p_last[pos]; sitd = sc->sc_isoc_fs_p_last[pos]; while(itd && max && max--) { ehci_dump_itd(itd); itd = itd->prev; } while(sitd && max && max--) { ehci_dump_sitd(sitd); sitd = sitd->prev; } return; } #endif #define EHCI_APPEND_FS_TD(std,last) (last) = _ehci_append_fs_td(std,last) static ehci_sitd_t * _ehci_append_fs_td(ehci_sitd_t *std, ehci_sitd_t *last) { DPRINTFN(10, ("%p to %p\n", std, last)); /* (sc->sc_bus.mtx) must be locked */ std->next = last->next; std->sitd_next = last->sitd_next; std->prev = last; /* the last->next->prev is never followed: * std->next->prev = std; */ last->next = std; last->sitd_next = std->sitd_self; return(std); } #define EHCI_APPEND_HS_TD(std,last) (last) = _ehci_append_hs_td(std,last) static ehci_itd_t * _ehci_append_hs_td(ehci_itd_t *std, ehci_itd_t *last) { DPRINTFN(10, ("%p to %p\n", std, last)); /* (sc->sc_bus.mtx) must be locked */ std->next = last->next; std->itd_next = last->itd_next; std->prev = last; /* the last->next->prev is never followed: * std->next->prev = std; */ last->next = std; last->itd_next = std->itd_self; return(std); } #define EHCI_APPEND_QH(sqh,last) (last) = _ehci_append_qh(sqh,last) static ehci_qh_t * _ehci_append_qh(ehci_qh_t *sqh, ehci_qh_t *last) { DPRINTFN(10, ("%p to %p\n", sqh, last)); /* (sc->sc_bus.mtx) must be locked */ sqh->next = last->next; sqh->qh_link = last->qh_link; sqh->prev = last; /* the last->next->prev is never followed: * sqh->next->prev = sqh; */ last->next = sqh; last->qh_link = sqh->qh_self; #ifdef USB_DEBUG if(ehcidebug > 5) { printf("%s:\n", __FUNCTION__); ehci_dump_sqh(sqh); } #endif return(sqh); } #define EHCI_REMOVE_FS_TD(std,last) (last) = _ehci_remove_fs_td(std,last) static ehci_sitd_t * _ehci_remove_fs_td(ehci_sitd_t *std, ehci_sitd_t *last) { DPRINTFN(10, ("%p from %p\n", std, last)); /* (sc->sc_bus.mtx) must be locked */ std->prev->next = std->next; std->prev->sitd_next = std->sitd_next; if(std->next) { std->next->prev = std->prev; } return((last == std) ? std->prev : last); } #define EHCI_REMOVE_HS_TD(std,last) (last) = _ehci_remove_hs_td(std,last) static ehci_itd_t * _ehci_remove_hs_td(ehci_itd_t *std, ehci_itd_t *last) { DPRINTFN(10, ("%p from %p\n", std, last)); /* (sc->sc_bus.mtx) must be locked */ std->prev->next = std->next; std->prev->itd_next = std->itd_next; if(std->next) { std->next->prev = std->prev; } return((last == std) ? std->prev : last); } #define EHCI_REMOVE_QH(sqh,last) (last) = _ehci_remove_qh(sqh,last) static ehci_qh_t * _ehci_remove_qh(ehci_qh_t *sqh, ehci_qh_t *last) { DPRINTFN(10, ("%p from %p\n", sqh, last)); /* (sc->sc_bus.mtx) must be locked */ /* only remove if not removed from a queue */ if(sqh->prev) { sqh->prev->next = sqh->next; sqh->prev->qh_link = sqh->qh_link; if(sqh->next) { sqh->next->prev = sqh->prev; } /* set the Terminate-bit in the e_next of the QH, * in case the transferred packet was short so * that the QH still points at the last used TD */ sqh->qh_qtd.qtd_next = htole32(EHCI_LINK_TERMINATE); last = ((last == sqh) ? sqh->prev : last); sqh->prev = 0; } return(last); } static void ehci_device_done(struct usbd_xfer *xfer, usbd_status error); static void ehci_non_isoc_done(struct usbd_xfer *xfer) { u_int32_t status = 0; u_int32_t actlen = 0; u_int16_t len; ehci_qtd_t *td = xfer->td_transfer_first; DPRINTFN(12, ("xfer=%p pipe=%p transfer done\n", xfer, xfer->pipe)); #ifdef USB_DEBUG if(ehcidebug > 10) { ehci_dump_sqtds(td); } #endif /* the transfer is done, compute actual length and status */ for (; td != NULL; td = ((td == xfer->td_transfer_last) ? NULL : td->obj_next)) { if(td->qtd_status & htole32(EHCI_QTD_ACTIVE)) { break; } status = le32toh(td->qtd_status); /* halt is ok if descriptor is last, and complete */ if((td->qtd_next == htole32(EHCI_LINK_TERMINATE)) && (EHCI_QTD_GET_BYTES(status) == 0)) { status &= ~EHCI_QTD_HALTED; } len = EHCI_QTD_GET_BYTES(status); if(len <= td->len) { actlen += td->len - len; } } /* if there are left over TDs * the toggle needs to be updated */ if(td != NULL) { xfer->pipe->toggle_next = (td->qtd_status & htole32(EHCI_QTD_SET_TOGGLE(1))) ? 1 : 0; } /* update toggle in case of * a short transfer */ xfer->pipe->toggle_next ^= (EHCI_QTD_GET_BYTES(status) / xfer->max_packet_size) & 1; DPRINTFN(10, ("actlen=%d\n", actlen)); xfer->actlen = actlen; status &= EHCI_QTD_STATERRS; #ifdef USB_DEBUG if(status == EHCI_QTD_HALTED) { DPRINTFN(10, ("error, addr=%d, endpt=0x%02x, " "status=%s%s%s%s%s%s%s%s\n", xfer->address, xfer->endpoint, (status & EHCI_QTD_ACTIVE) ? "ACTIVE" : "NOT_ACTIVE", (status & EHCI_QTD_HALTED) ? "-HALTED" : "", (status & EHCI_QTD_BUFERR) ? "-BUFERR" : "", (status & EHCI_QTD_BABBLE) ? "-BABBLE" : "", (status & EHCI_QTD_XACTERR) ? "-XACTERR" : "", (status & EHCI_QTD_MISSEDMICRO) ? "-MISSED" : "", (status & EHCI_QTD_SPLITXSTATE) ? "-SPLIT" : "", (status & EHCI_QTD_PINGSTATE) ? "-PING" : "")); } #endif ehci_device_done(xfer, (status == 0) ? USBD_NORMAL_COMPLETION : (status == EHCI_QTD_HALTED) ? USBD_STALLED : USBD_IOERROR); return; } /* returns one when transfer is finished * and callback must be called else zero */ static u_int8_t ehci_check_transfer(struct usbd_xfer *xfer, struct thread *ctd) { DPRINTFN(15, ("xfer=%p\n", xfer)); if(xfer->usb_thread != ctd) { /* cannot call this transfer * back due to locking ! */ goto done; } if(xfer->pipe->methods == &ehci_device_isoc_fs_methods) { ehci_sitd_t *td = xfer->td_transfer_last; /* isochronous full speed transfer */ if(!(td->sitd_status & htole32(EHCI_SITD_ACTIVE))) { ehci_device_done(xfer, USBD_NORMAL_COMPLETION); goto transferred; } } else if(xfer->pipe->methods == &ehci_device_isoc_hs_methods) { ehci_itd_t *td = xfer->td_transfer_last; /* isochronous high speed transfer */ if((!(td->itd_status[0] & htole32(EHCI_ITD_ACTIVE))) && (!(td->itd_status[1] & htole32(EHCI_ITD_ACTIVE))) && (!(td->itd_status[2] & htole32(EHCI_ITD_ACTIVE))) && (!(td->itd_status[3] & htole32(EHCI_ITD_ACTIVE))) && (!(td->itd_status[4] & htole32(EHCI_ITD_ACTIVE))) && (!(td->itd_status[5] & htole32(EHCI_ITD_ACTIVE))) && (!(td->itd_status[6] & htole32(EHCI_ITD_ACTIVE))) && (!(td->itd_status[7] & htole32(EHCI_ITD_ACTIVE)))) { ehci_device_done(xfer, USBD_NORMAL_COMPLETION); goto transferred; } } else { ehci_qtd_t *td = xfer->td_transfer_last; /* non-isochronous transfer */ if(td->qtd_status & htole32(EHCI_QTD_ACTIVE)) { /* * if the last TD is still active we need to * check whether there is an error somewhere * in the middle, or whether there was a short * packet (SPD and not ACTIVE) */ DPRINTFN(12, ("xfer=%p active\n", xfer)); for(td = xfer->td_transfer_cache; td != NULL; td = ((td == xfer->td_transfer_last) ? NULL : td->obj_next)) { u_int32_t status; status = le32toh(td->qtd_status); /* if there's an active TD * the transfer isn't done */ if(status & EHCI_QTD_ACTIVE) { DPRINTFN(12, ("xfer=%p is still " "active\n", xfer)); /* update cache */ xfer->td_transfer_cache = td; goto done; } /* any kind of error makes * the transfer done */ if(status & EHCI_QTD_HALTED) { break; } /* we want short packets, * and if it is short: it's done */ if(EHCI_QTD_GET_BYTES(status) != 0) { break; } } } ehci_non_isoc_done(xfer); goto transferred; } done: return 0; transferred: return 1; } static void ehci_pcd_enable(ehci_softc_t *sc) { struct usbd_callback_info info[1]; struct usbd_xfer *xfer; mtx_assert(&sc->sc_bus.mtx, MA_OWNED); sc->sc_eintrs |= EHCI_STS_PCD; EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs); /* acknowledge any PCD interrupt */ EOWRITE4(sc, EHCI_USBSTS, EHCI_STS_PCD); xfer = sc->sc_intrxfer; if(xfer) { /* transfer is transferred */ ehci_device_done(xfer, USBD_NORMAL_COMPLETION); /* queue callback */ info[0].xfer = xfer; info[0].refcount = xfer->usb_refcount; xfer->usb_root->memory_refcount++; mtx_unlock(&sc->sc_bus.mtx); usbd_do_callback(&info[0],&info[1]); } else { mtx_unlock(&sc->sc_bus.mtx); } return; } static void ehci_interrupt_td(ehci_softc_t *sc, struct thread *ctd) { enum { FINISH_LIST_MAX = 16 }; struct usbd_callback_info info[FINISH_LIST_MAX]; struct usbd_callback_info *ptr = &info[0]; struct usbd_xfer *xfer; u_int32_t status; u_int8_t need_repeat = 0; mtx_lock(&sc->sc_bus.mtx); /* * It can happen that an interrupt will be delivered to * us before the device has been fully attached and the * softc struct has been configured. Usually this happens * when kldloading the USB support as a module after the * system has been booted. If we detect this condition, * we need to squelch the unwanted interrupts until we're * ready for them. */ if(sc->sc_bus.bdev == NULL) { goto done; } if(ctd) { /* the poll thread should not read * any status registers that will * clear interrupts! */ goto repeat; } sc->sc_bus.no_intrs++; DPRINTFN(15,("%s: real interrupt\n", device_get_nameunit(sc->sc_bus.bdev))); #ifdef USB_DEBUG if(ehcidebug > 15) { DPRINTF(("%s\n", device_get_nameunit(sc->sc_bus.bdev))); ehci_dump_regs(sc); } #endif status = EHCI_STS_INTRS(EOREAD4(sc, EHCI_USBSTS)); if(status == 0) { /* the interrupt was not for us */ goto done; } if(!(status & sc->sc_eintrs)) { goto done; } EOWRITE4(sc, EHCI_USBSTS, status); /* acknowledge */ status &= sc->sc_eintrs; if(status & EHCI_STS_IAA) { DPRINTF(("door bell\n")); wakeup(&sc->sc_async_p_last); } if(status & EHCI_STS_HSE) { device_printf(sc->sc_bus.bdev, "unrecoverable error, " "controller halted\n"); #ifdef USB_DEBUG ehci_dump_regs(sc); ehci_dump_isoc(sc); #endif } if(status & EHCI_STS_PCD) { xfer = sc->sc_intrxfer; if(xfer) { ehci_device_done(xfer, USBD_NORMAL_COMPLETION); /* queue callback */ ptr->xfer = xfer; ptr->refcount = xfer->usb_refcount; ptr++; xfer->usb_root->memory_refcount++; } /* * Disable PCD interrupt for now, because it will be * on until the port has been reset. */ sc->sc_eintrs &= ~EHCI_STS_PCD; EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs); /* do not allow RHSC interrupts > 1 per second */ __callout_reset(&sc->sc_tmo_pcd, hz, (void *)(void *)ehci_pcd_enable, sc); } status &= ~(EHCI_STS_INT | EHCI_STS_ERRINT | EHCI_STS_PCD | EHCI_STS_IAA); if(status != 0) { /* block unprocessed interrupts */ sc->sc_eintrs &= ~status; EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs); device_printf(sc->sc_bus.bdev, "blocking intrs 0x%x\n", status); } repeat: LIST_FOREACH(xfer, &sc->sc_interrupt_list_head, interrupt_list) { /* check if transfer is * transferred */ if(ehci_check_transfer(xfer, ctd)) { /* queue callback */ ptr->xfer = xfer; ptr->refcount = xfer->usb_refcount; ptr++; xfer->usb_root->memory_refcount++; /* check queue length */ if(ptr >= &info[FINISH_LIST_MAX]) { need_repeat = 1; break; } } } done: mtx_unlock(&sc->sc_bus.mtx); usbd_do_callback(&info[0],ptr); if(need_repeat) { ptr = &info[0]; need_repeat = 0; mtx_lock(&sc->sc_bus.mtx); goto repeat; } return; } void ehci_interrupt(ehci_softc_t *sc) { ehci_interrupt_td(sc, NULL); return; } /* * called when a request does not complete */ static void ehci_timeout(struct usbd_xfer *xfer) { struct usbd_callback_info info[1]; ehci_softc_t *sc = xfer->usb_sc; DPRINTF(("xfer=%p\n", xfer)); mtx_assert(&sc->sc_bus.mtx, MA_OWNED); /* transfer is transferred */ ehci_device_done(xfer, USBD_TIMEOUT); /* queue callback */ info[0].xfer = xfer; info[0].refcount = xfer->usb_refcount; xfer->usb_root->memory_refcount++; mtx_unlock(&sc->sc_bus.mtx); usbd_do_callback(&info[0],&info[1]); return; } static void ehci_do_poll(struct usbd_bus *bus) { ehci_interrupt_td(EHCI_BUS2SC(bus), curthread); return; } #define ehci_add_interrupt_info(sc, xfer) \ LIST_INSERT_HEAD(&(sc)->sc_interrupt_list_head, (xfer), interrupt_list) static void ehci_remove_interrupt_info(struct usbd_xfer *xfer) { if((xfer)->interrupt_list.le_prev) { LIST_REMOVE((xfer), interrupt_list); (xfer)->interrupt_list.le_prev = NULL; } return; } static void ehci_setup_standard_chain(struct usbd_xfer *xfer, ehci_qh_t **qh_last) { struct usbd_page_search buf_res; /* the EHCI hardware can handle at most five 4k crossing per TD */ u_int32_t average = (EHCI_PAGE_SIZE - (EHCI_PAGE_SIZE % xfer->max_packet_size)); u_int32_t qtd_status; u_int32_t buf_offset; u_int32_t len = xfer->length; u_int32_t c_error = (xfer->udev->speed == USB_SPEED_HIGH) ? 0 : htole32(EHCI_QTD_SET_CERR(3)); u_int8_t isread; u_int8_t shortpkt = 0; ehci_qtd_t *td; ehci_qtd_t *td_last = NULL; ehci_qh_t *qh; DPRINTFN(8, ("addr=%d endpt=%d len=%d speed=%d\n", xfer->address, UE_GET_ADDR(xfer->endpoint), xfer->length, xfer->udev->speed)); td = (xfer->td_transfer_first = xfer->td_transfer_cache = xfer->td_start); buf_offset = 0; usbd_get_page(&(xfer->buf_data), buf_offset, &buf_res); if(xfer->pipe->methods == &ehci_device_ctrl_methods) { /* the first byte is "bmRequestType" */ isread = *((u_int8_t *)(buf_res.buffer)); isread &= UT_READ; /* * check length ? */ xfer->pipe->toggle_next = 1; /* SETUP message */ td->qtd_status = c_error | htole32 (EHCI_QTD_ACTIVE | EHCI_QTD_SET_PID(EHCI_QTD_PID_SETUP) | EHCI_QTD_SET_TOGGLE(0) | EHCI_QTD_SET_BYTES(sizeof(usb_device_request_t))); td->qtd_buffer[0] = htole32(buf_res.physaddr); td->qtd_buffer_hi[0] = 0; buf_offset += sizeof(usb_device_request_t); usbd_get_page(&(xfer->buf_data), buf_offset, &buf_res); td->qtd_buffer[1] = htole32(buf_res.physaddr & (~0xFFF)); td->qtd_buffer_hi[1] = 0; td->len = sizeof(usb_device_request_t); len -= sizeof(usb_device_request_t); td_last = td; td = td->obj_next; } else { isread = (UE_GET_DIR(xfer->endpoint) == UE_DIR_IN); if(xfer->length == 0) { /* must allow access to "td_last", * so xfer->length cannot be zero! */ printf("%s: setting USBD_FORCE_SHORT_XFER!\n", __FUNCTION__); xfer->flags |= USBD_FORCE_SHORT_XFER; } } qtd_status = c_error | (isread ? htole32 (EHCI_QTD_ACTIVE | EHCI_QTD_SET_PID(EHCI_QTD_PID_IN)) : htole32 (EHCI_QTD_ACTIVE | EHCI_QTD_SET_PID(EHCI_QTD_PID_OUT))); if(xfer->pipe->toggle_next) { qtd_status |= htole32(EHCI_QTD_SET_TOGGLE(1)); } while(1) { if(len == 0) { if(xfer->flags & USBD_FORCE_SHORT_XFER) { if(shortpkt) { break; } } else { break; } } if(len < average) { if((len % xfer->max_packet_size) || (len == 0)) { shortpkt = 1; } average = len; } if(td == NULL) { panic("%s: software wants to write more data " "than there is in the buffer!", __FUNCTION__); } /* link in last TD */ if (td_last) { td_last->qtd_next = td->qtd_self; /* short transfers should terminate the transfer: */ td_last->qtd_altnext = htole32(EHCI_LINK_TERMINATE); } /* fill out current TD */ td->qtd_status = qtd_status | htole32(EHCI_QTD_SET_BYTES(average)); td->qtd_buffer[0] = htole32(buf_res.physaddr); td->qtd_buffer_hi[0] = 0; buf_offset += average; usbd_get_page(&(xfer->buf_data), buf_offset, &buf_res); td->qtd_buffer[1] = htole32(buf_res.physaddr & (~0xFFF)); td->qtd_buffer_hi[1] = 0; td->len = average; /* adjust the toggle based on the * number of packets in this qtd */ if((((average + xfer->max_packet_size - 1) / xfer->max_packet_size) & 1) || (!average)) { xfer->pipe->toggle_next = xfer->pipe->toggle_next ? 0 : 1; qtd_status ^= htole32(EHCI_QTD_TOGGLE_MASK); } len -= average; td_last = td; td = td->obj_next; } if(xfer->pipe->methods == &ehci_device_ctrl_methods) { /* link in last TD */ td_last->qtd_next = td->qtd_self; /* short transfers should terminate the transfer: */ td_last->qtd_altnext = htole32(EHCI_LINK_TERMINATE); /* STATUS message */ td->qtd_status = c_error | (isread ? htole32 (EHCI_QTD_ACTIVE | EHCI_QTD_SET_PID(EHCI_QTD_PID_OUT) | EHCI_QTD_SET_TOGGLE(1) | EHCI_QTD_IOC) : htole32 (EHCI_QTD_ACTIVE | EHCI_QTD_SET_PID(EHCI_QTD_PID_IN) | EHCI_QTD_SET_TOGGLE(1) | EHCI_QTD_IOC)); td->qtd_buffer[0] = 0; td->qtd_buffer_hi[0] = 0; td->len = 0; td_last = td; } td_last->qtd_next = htole32(EHCI_LINK_TERMINATE); td_last->qtd_altnext = htole32(EHCI_LINK_TERMINATE); td_last->qtd_status |= htole32(EHCI_QTD_IOC); /* must have at least one frame! */ xfer->td_transfer_last = td_last; #ifdef USB_DEBUG if(ehcidebug > 8) { DPRINTF(("nexttog=%d; data before transfer:\n", xfer->pipe->toggle_next)); ehci_dump_sqtds(xfer->td_start); } #endif qh = xfer->qh_start; /* the "qh_link" field is filled when the QH is added */ qh->qh_endp = htole32 (EHCI_QH_SET_ADDR(xfer->address) | EHCI_QH_SET_ENDPT(UE_GET_ADDR(xfer->endpoint)) | EHCI_QH_DTC | EHCI_QH_SET_MPL(xfer->max_packet_size) | EHCI_QH_SET_NRL(8) /* XXX */ ); switch (xfer->udev->speed) { case USB_SPEED_LOW: qh->qh_endp |= htole32(EHCI_QH_SET_EPS(EHCI_QH_SPEED_LOW)| EHCI_QH_CTL); break; case USB_SPEED_FULL: qh->qh_endp |= htole32(EHCI_QH_SET_EPS(EHCI_QH_SPEED_FULL)| EHCI_QH_CTL); break; case USB_SPEED_HIGH: qh->qh_endp |= htole32(EHCI_QH_SET_EPS(EHCI_QH_SPEED_HIGH)); break; default: panic("%s: bad device speed %d!", __FUNCTION__, xfer->udev->speed); break; } if(xfer->pipe->methods != &ehci_device_ctrl_methods) { qh->qh_endp &= htole32(~EHCI_QH_CTL); } qh->qh_endphub = htole32 (EHCI_QH_SET_MULT(1)| EHCI_QH_SET_CMASK(0xf0)); if(xfer->udev->myhsport) { qh->qh_endphub |= htole32 (EHCI_QH_SET_HUBA(xfer->udev->myhsport->parent->address)| EHCI_QH_SET_PORT(xfer->udev->myhsport->portno)); } if(xfer->pipe->methods == &ehci_device_intr_methods) { /* execute the transfer one time per 1ms */ qh->qh_endphub |= htole32(EHCI_QH_SET_SMASK(0x04)); } qh->qh_curqtd = htole32(0); /* fill the overlay qTD */ qh->qh_qtd.qtd_status = htole32(0); td = xfer->td_transfer_first; qh->qh_qtd.qtd_next = td->qtd_self; qh->qh_qtd.qtd_altnext = htole32(EHCI_LINK_TERMINATE); EHCI_APPEND_QH(qh, *qh_last); return; } static void ehci_root_intr_done(ehci_softc_t *sc, struct usbd_xfer *xfer) { struct usbd_page_search buf_res; u_int8_t *p; u_int16_t i; u_int16_t m; if(sc->sc_intrxfer) { /* disable further interrupts */ sc->sc_intrxfer = NULL; /* clear all bits */ usbd_bzero(&(xfer->buf_data), 0, xfer->length); /* set bits */ m = (xfer->length * 8); i = (sc->sc_noport + 1); m = min(m,i); for(i = 1; i < m; i++) { /* pick out CHANGE bits from the status register */ if(EOREAD4(sc, EHCI_PORTSC(i)) & EHCI_PS_CLEAR) { usbd_get_page(&(xfer->buf_data), i/8, &buf_res); p = buf_res.buffer; *p |= 1 << (i % 8); DPRINTF(("port %d changed\n", i)); } } xfer->actlen = xfer->length; } return; } static u_int8_t ehci_isoc_fs_done(ehci_softc_t *sc, struct usbd_xfer *xfer) { u_int32_t nframes = xfer->nframes; u_int32_t actlen = 0; u_int16_t *plen = xfer->frlengths; u_int16_t len = 0; u_int8_t need_delay = 0; ehci_sitd_t *td = xfer->td_transfer_first; ehci_sitd_t **pp_last = &sc->sc_isoc_fs_p_last[xfer->qh_pos]; DPRINTFN(12, ("xfer=%p pipe=%p transfer done\n", xfer, xfer->pipe)); while(nframes--) { if(td == NULL) { panic("%s:%d: out of TD's\n", __FUNCTION__, __LINE__); } if(pp_last >= &sc->sc_isoc_fs_p_last[EHCI_VIRTUAL_FRAMELIST_COUNT]) { pp_last = &sc->sc_isoc_fs_p_last[0]; } #ifdef USB_DEBUG if(ehcidebug > 15) { DPRINTFN(15,("isoc FS-TD\n")); ehci_dump_sitd(td); } #endif /* check for active transfers */ if(td->sitd_status & htole32(EHCI_SITD_ACTIVE)) { need_delay = 1; } len = EHCI_SITD_GET_LEN(le32toh(td->sitd_status)); if(*plen >= len) { len = *plen - len; } else { len = 0; } *plen = len; actlen += len; /* remove FS-TD from schedule */ EHCI_REMOVE_FS_TD(td, *pp_last); pp_last++; plen++; td = td->obj_next; } xfer->actlen = actlen; return need_delay; } static u_int8_t ehci_isoc_hs_done(ehci_softc_t *sc, struct usbd_xfer *xfer) { u_int32_t nframes = xfer->nframes; u_int32_t actlen = 0; u_int16_t *plen = xfer->frlengths; u_int16_t len = 0; u_int8_t td_no = 0; u_int8_t need_delay = 0; ehci_itd_t *td = xfer->td_transfer_first; ehci_itd_t **pp_last = &sc->sc_isoc_hs_p_last[xfer->qh_pos]; DPRINTFN(12, ("xfer=%p pipe=%p transfer done\n", xfer, xfer->pipe)); while(nframes--) { if(td == NULL) { panic("%s:%d: out of TD's\n", __FUNCTION__, __LINE__); } if(pp_last >= &sc->sc_isoc_hs_p_last[EHCI_VIRTUAL_FRAMELIST_COUNT]) { pp_last = &sc->sc_isoc_hs_p_last[0]; } #ifdef USB_DEBUG if(ehcidebug > 15) { DPRINTFN(15,("isoc HS-TD\n")); ehci_dump_itd(td); } #endif if(td->itd_status[td_no] & htole32(EHCI_ITD_ACTIVE)) { need_delay = 1; } len = EHCI_ITD_GET_LEN(le32toh(td->itd_status[td_no])); if(*plen >= len) { len = *plen - len; } else { len = 0; } *plen = len; actlen += len; plen++; td_no++; if((td_no == 8) || (nframes == 0)) { /* remove HS-TD from schedule */ EHCI_REMOVE_HS_TD(td, *pp_last); pp_last++; td_no = 0; td = td->obj_next; } } xfer->actlen = actlen; return need_delay; } /* NOTE: "done" can be run two times in a row, * from close and from interrupt */ static void ehci_device_done(struct usbd_xfer *xfer, usbd_status error) { ehci_softc_t *sc = xfer->usb_sc; u_int8_t need_delay; mtx_assert(&sc->sc_bus.mtx, MA_OWNED); need_delay = 0; DPRINTFN(1,("xfer=%p, pipe=%p length=%d error=%d\n", xfer, xfer->pipe, xfer->actlen, error)); /* ... could check for not-completed transfers, * instead of setting need_delay ... */ if((error == USBD_CANCELLED) || (error == USBD_TIMEOUT)) { if(xfer->flags & USBD_DEV_TRANSFERRING) { need_delay = 1; } } if((xfer->pipe->methods == &ehci_device_bulk_methods) || (xfer->pipe->methods == &ehci_device_ctrl_methods)) { #ifdef USB_DEBUG if(ehcidebug > 8) { DPRINTF(("nexttog=%d; data after transfer:\n", xfer->pipe->toggle_next)); ehci_dump_sqtds(xfer->td_start); } #endif EHCI_REMOVE_QH(xfer->qh_start, sc->sc_async_p_last); } if(xfer->pipe->methods == &ehci_device_intr_methods) { EHCI_REMOVE_QH(xfer->qh_start, sc->sc_intr_p_last[xfer->qh_pos]); } /* finish isochronous transfers * (will update xfer->actlen and xfer->frlengths; * should only be called once) */ if(xfer->td_transfer_first && xfer->td_transfer_last) { if(xfer->pipe->methods == &ehci_device_isoc_fs_methods) { if(ehci_isoc_fs_done(sc, xfer)) { need_delay = 1; } } if(xfer->pipe->methods == &ehci_device_isoc_hs_methods) { if(ehci_isoc_hs_done(sc, xfer)) { need_delay = 1; } } xfer->td_transfer_first = NULL; xfer->td_transfer_last = NULL; } /* finish root interrupt transfer * (will update xfer->buffer and xfer->actlen) */ if(xfer->pipe->methods == &ehci_root_intr_methods) { ehci_root_intr_done(sc, xfer); } /* stop timeout */ __callout_stop(&xfer->timeout_handle); /* remove interrupt info (if any) */ ehci_remove_interrupt_info(xfer); if ((xfer->pipe->methods != &ehci_root_ctrl_methods) && (xfer->pipe->methods != &ehci_root_intr_methods)) { if(((xfer->pipe->methods == &ehci_device_ctrl_methods) || (xfer->pipe->methods == &ehci_device_bulk_methods)) && (sc->sc_doorbell_disable == 0)) { u_int32_t to = 100*1000; /* * This implementation does not use the doorbell * interrupt. The reason is that one gets more * throughput by waiting for the doorbell inline, * than by using the doorbell interrupt. A typical * result reading from an USB mass storage disk: * * With doorbell interrupt: 27 seconds for 512MB * With inline doorbell: 21 seconds for 512MB * * Although the inline version causes a slightly * higher CPU usage, it does not block the system * in any way, because this USB driver uses its * own mutex. * * --hps */ /* wait for doorbell ~32us */ EOWRITE4(sc, EHCI_USBCMD, EOREAD4(sc, EHCI_USBCMD) | EHCI_CMD_IAAD); while(EOREAD4(sc, EHCI_USBCMD) & EHCI_CMD_IAAD) { if(!to--) { printf("%s: doorbell timeout " "(disabling)\n", __FUNCTION__); sc->sc_doorbell_disable = 1; break; } DELAY(1); } /* acknowledge any doorbell interrupt * (SiS chipsets require this) */ EOWRITE4(sc, EHCI_USBSTS, EHCI_STS_IAA); } else { /* wait until the hardware has finished any possible * use of the transfer descriptor and QH * * in case "need_delay" is set, * wait until the next isochronous * frame is started */ DELAY(need_delay ? ((3*1000)/(2*8)) : (5)); } } if(error) { /* next transfer needs to clear stall */ xfer->pipe->clearstall = 1; } /* transfer transferred (no callback!) */ usbd_transfer_done(xfer,error); /* dequeue transfer (and start next transfer) * * if two transfers are queued, the second * transfer must be started before the * first is called back! */ usbd_transfer_dequeue(xfer); return; } /*---------------------------------------------------------------------------* * ehci bulk support *---------------------------------------------------------------------------*/ static void ehci_device_bulk_open(struct usbd_xfer *xfer) { return; } static void ehci_device_bulk_close(struct usbd_xfer *xfer) { ehci_device_done(xfer, USBD_CANCELLED); return; } static void ehci_device_bulk_enter(struct usbd_xfer *xfer) { /* enqueue transfer */ usbd_transfer_enqueue(xfer); return; } static void ehci_device_bulk_start(struct usbd_xfer *xfer) { ehci_softc_t *sc = xfer->usb_sc; DPRINTFN(3, ("xfer=%p len=%d\n", xfer, xfer->length)); /* setup TD's and QH */ ehci_setup_standard_chain(xfer, &sc->sc_async_p_last); /**/ ehci_add_interrupt_info(sc, xfer); if(xfer->timeout && (!(xfer->flags & USBD_USE_POLLING))) { __callout_reset(&xfer->timeout_handle, MS_TO_TICKS(xfer->timeout), (void *)(void *)ehci_timeout, xfer); } return; } struct usbd_pipe_methods ehci_device_bulk_methods = { .open = ehci_device_bulk_open, .close = ehci_device_bulk_close, .enter = ehci_device_bulk_enter, .start = ehci_device_bulk_start, .copy_in = usbd_std_bulk_intr_copy_in, .copy_out = usbd_std_bulk_intr_copy_out, }; /*---------------------------------------------------------------------------* * ehci control support *---------------------------------------------------------------------------*/ static void ehci_device_ctrl_open(struct usbd_xfer *xfer) { return; } static void ehci_device_ctrl_close(struct usbd_xfer *xfer) { ehci_device_done(xfer, USBD_CANCELLED); return; } static void ehci_device_ctrl_enter(struct usbd_xfer *xfer) { /* enqueue transfer */ usbd_transfer_enqueue(xfer); return; } static void ehci_device_ctrl_start(struct usbd_xfer *xfer) { ehci_softc_t *sc = xfer->usb_sc; /* setup TD's and QH */ ehci_setup_standard_chain(xfer, &sc->sc_async_p_last); /**/ ehci_add_interrupt_info(sc, xfer); if(xfer->timeout && (!(xfer->flags & USBD_USE_POLLING))) { __callout_reset(&xfer->timeout_handle, MS_TO_TICKS(xfer->timeout), (void *)(void *)ehci_timeout, xfer); } return; } struct usbd_pipe_methods ehci_device_ctrl_methods = { .open = ehci_device_ctrl_open, .close = ehci_device_ctrl_close, .enter = ehci_device_ctrl_enter, .start = ehci_device_ctrl_start, .copy_in = usbd_std_ctrl_copy_in, .copy_out = usbd_std_ctrl_copy_out, }; /*---------------------------------------------------------------------------* * ehci interrupt support *---------------------------------------------------------------------------*/ static void ehci_device_intr_open(struct usbd_xfer *xfer) { ehci_softc_t *sc = xfer->usb_sc; u_int16_t best; u_int16_t bit; u_int16_t x; best = 0; bit = EHCI_VIRTUAL_FRAMELIST_COUNT/2; while(bit) { if(xfer->interval >= bit) { x = bit; best = bit; while(x & bit) { if(sc->sc_intr_stat[x] < sc->sc_intr_stat[best]) { best = x; } x++; } break; } bit >>= 1; } sc->sc_intr_stat[best]++; xfer->qh_pos = best; DPRINTFN(2, ("best=%d interval=%d\n", best, xfer->interval)); return; } static void ehci_device_intr_close(struct usbd_xfer *xfer) { ehci_softc_t *sc = xfer->usb_sc; sc->sc_intr_stat[xfer->qh_pos]--; ehci_device_done(xfer, USBD_CANCELLED); return; } static void ehci_device_intr_enter(struct usbd_xfer *xfer) { /* enqueue transfer */ usbd_transfer_enqueue(xfer); return; } static void ehci_device_intr_start(struct usbd_xfer *xfer) { ehci_softc_t *sc = xfer->usb_sc; DPRINTFN(3,("xfer=%p len=%d\n", xfer, xfer->length)); /* setup TD's and QH */ ehci_setup_standard_chain(xfer, &sc->sc_intr_p_last[xfer->qh_pos]); /**/ ehci_add_interrupt_info(sc, xfer); if(xfer->timeout && (!(xfer->flags & USBD_USE_POLLING))) { __callout_reset(&xfer->timeout_handle, MS_TO_TICKS(xfer->timeout), (void *)(void *)ehci_timeout, xfer); } return; } struct usbd_pipe_methods ehci_device_intr_methods = { .open = ehci_device_intr_open, .close = ehci_device_intr_close, .enter = ehci_device_intr_enter, .start = ehci_device_intr_start, .copy_in = usbd_std_bulk_intr_copy_in, .copy_out = usbd_std_bulk_intr_copy_out, }; /*---------------------------------------------------------------------------* * ehci full speed isochronous support *---------------------------------------------------------------------------*/ static void ehci_device_isoc_fs_open(struct usbd_xfer *xfer) { ehci_sitd_t *td; u_int32_t sitd_portaddr; sitd_portaddr = EHCI_SITD_SET_ADDR(xfer->address)| EHCI_SITD_SET_ENDPT(UE_GET_ADDR(xfer->endpoint)); if(UE_GET_DIR(xfer->endpoint) == UE_DIR_IN) { sitd_portaddr |= EHCI_SITD_SET_DIR_IN; } if(xfer->udev->myhsport) { sitd_portaddr |= EHCI_SITD_SET_HUBA(xfer->udev->myhsport->parent->address)| EHCI_SITD_SET_PORT(xfer->udev->myhsport->portno); } sitd_portaddr = htole32(sitd_portaddr); /* initialize all TD's */ for(td = xfer->td_start; td; td = td->obj_next) { td->sitd_portaddr = sitd_portaddr; /* TODO: make some kind of automatic SMASK/CMASK selection * based on micro-frame usage * * micro-frame usage * (8 microframes per 1ms) * * 0: isoc-IN * 1: isoc-OUT * 2: interrupt transfers * . * . * 7: */ if(UE_GET_DIR(xfer->endpoint) == UE_DIR_IN) { td->sitd_mask = htole32(EHCI_SITD_SET_SMASK(0x01)| EHCI_SITD_SET_CMASK(0xFC)); } else { td->sitd_mask = htole32(EHCI_SITD_SET_SMASK(0x02)| EHCI_SITD_SET_CMASK(0xF8)); } td->sitd_back = htole32(EHCI_LINK_TERMINATE); } return; } static void ehci_device_isoc_fs_close(struct usbd_xfer *xfer) { ehci_device_done(xfer, USBD_CANCELLED); return; } static void ehci_device_isoc_fs_enter(struct usbd_xfer *xfer) { struct usbd_page_search buf_res; ehci_softc_t *sc = xfer->usb_sc; u_int32_t buf_offset; u_int32_t nframes; u_int16_t *plen; #ifdef USB_DEBUG u_int8_t once = 1; #endif ehci_sitd_t *td; ehci_sitd_t *td_last = NULL; ehci_sitd_t **pp_last; DPRINTFN(5,("xfer=%p next=%d nframes=%d\n", xfer, xfer->pipe->isoc_next, xfer->nframes)); nframes = EOREAD4(sc, EHCI_FRINDEX) / 8; if(((nframes - xfer->pipe->isoc_next) & (EHCI_VIRTUAL_FRAMELIST_COUNT-1)) < xfer->nframes) { /* not in use yet, schedule it a few frames ahead */ /* data underflow */ xfer->pipe->isoc_next = (nframes + 3) & (EHCI_VIRTUAL_FRAMELIST_COUNT-1); DPRINTFN(2,("start next=%d\n", xfer->pipe->isoc_next)); } nframes = xfer->nframes; if(nframes == 0) { /* transfer transferred */ ehci_device_done(xfer, USBD_NORMAL_COMPLETION); /* call callback recursively */ __usbd_callback(xfer); return; } buf_offset = 0; usbd_get_page(&(xfer->buf_data), buf_offset, &buf_res); plen = xfer->frlengths; td = (xfer->td_transfer_first = xfer->td_start); pp_last = &sc->sc_isoc_fs_p_last[xfer->pipe->isoc_next]; /* store starting position */ xfer->qh_pos = xfer->pipe->isoc_next; while(nframes--) { if(td == NULL) { panic("%s:%d: out of TD's\n", __FUNCTION__, __LINE__); } if(pp_last >= &sc->sc_isoc_fs_p_last[EHCI_VIRTUAL_FRAMELIST_COUNT]) { pp_last = &sc->sc_isoc_fs_p_last[0]; } /* reuse sitd_portaddr and sitd_back from last transfer */ /* TODO: implement support for multiple transactions */ if(*plen > xfer->max_frame_size) { #ifdef USB_DEBUG if(once) { once = 0; printf("%s: frame length(%d) exceeds %d " "bytes (frame truncated)\n", __FUNCTION__, *plen, xfer->max_frame_size); } #endif *plen = xfer->max_frame_size; } td->sitd_bp[0] = htole32(buf_res.physaddr); buf_offset += *plen; usbd_get_page(&(xfer->buf_data), buf_offset, &buf_res); td->sitd_bp[1] = htole32(buf_res.physaddr & (~0xFFF)); if(UE_GET_DIR(xfer->endpoint) == UE_DIR_OUT) { td->sitd_bp[1] |= htole32(1); /* T-count == 1 */ } if(nframes == 0) { td->sitd_status = htole32 (EHCI_SITD_IOC| EHCI_SITD_ACTIVE| EHCI_SITD_SET_LEN(*plen)); } else { td->sitd_status = htole32 (EHCI_SITD_ACTIVE| EHCI_SITD_SET_LEN(*plen)); } #ifdef USB_DEBUG if(ehcidebug > 15) { DPRINTFN(15,("FS-TD %d\n", nframes)); ehci_dump_sitd(td); } #endif /* insert TD into schedule */ EHCI_APPEND_FS_TD(td, *pp_last); pp_last++; plen++; td_last = td; td = td->obj_next; } xfer->td_transfer_last = td_last; /* update isoc_next */ xfer->pipe->isoc_next = (pp_last - &sc->sc_isoc_fs_p_last[0]) & (EHCI_VIRTUAL_FRAMELIST_COUNT-1); /**/ ehci_add_interrupt_info(sc, xfer); if(xfer->timeout && (!(xfer->flags & USBD_USE_POLLING))) { __callout_reset(&xfer->timeout_handle, MS_TO_TICKS(xfer->timeout), (void *)(void *)ehci_timeout, xfer); } /* enqueue transfer * (so that it can be aborted through pipe abort) */ usbd_transfer_enqueue(xfer); return; } static void ehci_device_isoc_fs_start(struct usbd_xfer *xfer) { /* already started, nothing to do */ return; } struct usbd_pipe_methods ehci_device_isoc_fs_methods = { .open = ehci_device_isoc_fs_open, .close = ehci_device_isoc_fs_close, .enter = ehci_device_isoc_fs_enter, .start = ehci_device_isoc_fs_start, .copy_in = usbd_std_isoc_copy_in, .copy_out = usbd_std_isoc_copy_out, }; /*---------------------------------------------------------------------------* * ehci high speed isochronous support *---------------------------------------------------------------------------*/ static void ehci_device_isoc_hs_open(struct usbd_xfer *xfer) { ehci_itd_t *td; /* initialize all TD's */ for(td = xfer->td_start; td; td = td->obj_next) { /* set TD inactive */ td->itd_status[0] = 0; td->itd_status[1] = 0; td->itd_status[2] = 0; td->itd_status[3] = 0; td->itd_status[4] = 0; td->itd_status[5] = 0; td->itd_status[6] = 0; td->itd_status[7] = 0; /* set endpoint and address */ td->itd_bp[0] = htole32 (EHCI_ITD_SET_ADDR(xfer->address)| EHCI_ITD_SET_ENDPT(UE_GET_ADDR(xfer->endpoint))); /* set maximum packet size */ td->itd_bp[1] = htole32 (EHCI_ITD_SET_MPL(xfer->max_packet_size & 0x7FF)); /* set direction */ if(UE_GET_DIR(xfer->endpoint) == UE_DIR_IN) { td->itd_bp[1] |= htole32(EHCI_ITD_SET_DIR_IN); } /* set transfer multiplier */ td->itd_bp[2] = htole32(1); } return; } static void ehci_device_isoc_hs_close(struct usbd_xfer *xfer) { ehci_device_done(xfer, USBD_CANCELLED); return; } static void ehci_device_isoc_hs_enter(struct usbd_xfer *xfer) { struct usbd_page_search buf_res; ehci_softc_t *sc = xfer->usb_sc; bus_size_t page_addr; u_int32_t status; u_int32_t buf_offset; u_int32_t nframes; u_int16_t *plen; u_int8_t page_no; u_int8_t td_no; #ifdef USB_DEBUG u_int8_t once = 1; #endif ehci_itd_t *td; ehci_itd_t *td_last = NULL; ehci_itd_t **pp_last; DPRINTFN(5,("xfer=%p next=%d nframes=%d\n", xfer, xfer->pipe->isoc_next, xfer->nframes)); nframes = EOREAD4(sc, EHCI_FRINDEX) / 8; if(((nframes - xfer->pipe->isoc_next) & (EHCI_VIRTUAL_FRAMELIST_COUNT-1)) < xfer->nframes) { /* not in use yet, schedule it a few frames ahead */ /* data underflow */ xfer->pipe->isoc_next = (nframes + 3) & (EHCI_VIRTUAL_FRAMELIST_COUNT-1); DPRINTFN(2,("start next=%d\n", xfer->pipe->isoc_next)); } nframes = xfer->nframes; if(nframes == 0) { /* transfer transferred */ ehci_device_done(xfer, USBD_NORMAL_COMPLETION); /* call callback recursively */ __usbd_callback(xfer); return; } buf_offset = 0; usbd_get_page(&(xfer->buf_data), buf_offset, &buf_res); page_addr = buf_res.physaddr & ~0xFFF; page_no = 0; td_no = 0; plen = xfer->frlengths; td = (xfer->td_transfer_first = xfer->td_start); pp_last = &sc->sc_isoc_hs_p_last[xfer->pipe->isoc_next]; /* store starting position */ xfer->qh_pos = xfer->pipe->isoc_next; while(nframes--) { if(td == NULL) { panic("%s:%d: out of TD's\n", __FUNCTION__, __LINE__); } if(pp_last >= &sc->sc_isoc_hs_p_last[EHCI_VIRTUAL_FRAMELIST_COUNT]) { pp_last = &sc->sc_isoc_hs_p_last[0]; } /* range check */ if(*plen > xfer->max_frame_size) { #ifdef USB_DEBUG if(once) { once = 0; printf("%s: frame length(%d) exceeds %d bytes " "(frame truncated)\n", __FUNCTION__, *plen, xfer->max_frame_size); } #endif *plen = xfer->max_frame_size; } if(td_no == 0) { /* update page address */ td->itd_bp[page_no] &= htole32(0xFFF); td->itd_bp[page_no] |= htole32(page_addr); if(nframes < 7) { /* clear all status in case * some are not initialized */ td->itd_status[0] = 0; td->itd_status[1] = 0; td->itd_status[2] = 0; td->itd_status[3] = 0; td->itd_status[4] = 0; td->itd_status[5] = 0; td->itd_status[6] = 0; td->itd_status[7] = 0; } } /* compute status */ if(nframes == 0) { status = (EHCI_ITD_SET_LEN(*plen)| EHCI_ITD_ACTIVE| EHCI_ITD_IOC| EHCI_ITD_SET_PG(page_no)| (buf_res.physaddr & 0xFFF)); } else { status = (EHCI_ITD_SET_LEN(*plen)| EHCI_ITD_ACTIVE| EHCI_ITD_SET_PG(page_no)| (buf_res.physaddr & 0xFFF)); } buf_offset += *plen; usbd_get_page(&(xfer->buf_data), buf_offset, &buf_res); if((buf_res.physaddr ^ page_addr) & ~0xFFF) { /* new page needed */ page_addr = buf_res.physaddr & ~0xFFF; page_no++; if(page_no < 7) { /* update page address */ td->itd_bp[page_no] &= htole32(0xFFF); td->itd_bp[page_no] |= htole32(page_addr); } } if(page_no < 7) { /* activate the transfer */ td->itd_status[td_no] = htole32(status); } else { /* pretend that the transfer has finished */ td->itd_status[td_no] = (nframes == 0) ? htole32(EHCI_ITD_IOC) : 0; #ifdef USB_DEBUG if(once) { once = 0; printf("%s: isoc limit reached! " "Max %d bytes per 8 frames. Frame skipped.\n", __FUNCTION__, (6 << 12)); } #endif } plen++; td_no++; if((td_no == 8) || (nframes == 0)) { #ifdef USB_DEBUG if(ehcidebug > 15) { DPRINTFN(15,("HS-TD %d\n", nframes)); ehci_dump_itd(td); } #endif /* insert TD into schedule */ EHCI_APPEND_HS_TD(td, *pp_last); pp_last++; page_no = 0; td_no = 0; td_last = td; td = td->obj_next; } } xfer->td_transfer_last = td_last; /* update isoc_next */ xfer->pipe->isoc_next = (pp_last - &sc->sc_isoc_hs_p_last[0]) & (EHCI_VIRTUAL_FRAMELIST_COUNT-1); /**/ ehci_add_interrupt_info(sc, xfer); if(xfer->timeout && (!(xfer->flags & USBD_USE_POLLING))) { __callout_reset(&xfer->timeout_handle, MS_TO_TICKS(xfer->timeout), (void *)(void *)ehci_timeout, xfer); } /* enqueue transfer * (so that it can be aborted through pipe abort) */ usbd_transfer_enqueue(xfer); return; } static void ehci_device_isoc_hs_start(struct usbd_xfer *xfer) { /* already started, nothing to do */ return; } struct usbd_pipe_methods ehci_device_isoc_hs_methods = { .open = ehci_device_isoc_hs_open, .close = ehci_device_isoc_hs_close, .enter = ehci_device_isoc_hs_enter, .start = ehci_device_isoc_hs_start, .copy_in = usbd_std_isoc_copy_in, .copy_out = usbd_std_isoc_copy_out, }; /*---------------------------------------------------------------------------* * ehci root control support *---------------------------------------------------------------------------* * simulate a hardware hub by handling * all the necessary requests *---------------------------------------------------------------------------*/ static void ehci_root_ctrl_open(struct usbd_xfer *xfer) { return; } static void ehci_root_ctrl_close(struct usbd_xfer *xfer) { ehci_device_done(xfer, USBD_CANCELLED); return; } /* data structures and routines * to emulate the root hub: */ static const usb_device_descriptor_t ehci_devd = { sizeof(usb_device_descriptor_t), UDESC_DEVICE, /* type */ {0x00, 0x02}, /* USB version */ UDCLASS_HUB, /* class */ UDSUBCLASS_HUB, /* subclass */ UDPROTO_HSHUBSTT, /* protocol */ 64, /* max packet */ {0},{0},{0x00,0x01}, /* device id */ 1,2,0, /* string indicies */ 1 /* # of configurations */ }; static const usb_device_qualifier_t ehci_odevd = { sizeof(usb_device_qualifier_t), UDESC_DEVICE_QUALIFIER, /* type */ {0x00, 0x02}, /* USB version */ UDCLASS_HUB, /* class */ UDSUBCLASS_HUB, /* subclass */ UDPROTO_FSHUB, /* protocol */ 64, /* max packet */ 1, /* # of configurations */ 0 }; static const usb_config_descriptor_t ehci_confd = { sizeof(usb_config_descriptor_t), UDESC_CONFIG, {USB_CONFIG_DESCRIPTOR_SIZE + USB_INTERFACE_DESCRIPTOR_SIZE + USB_ENDPOINT_DESCRIPTOR_SIZE}, 1, 1, 0, UC_SELF_POWERED, 0 /* max power */ }; static const usb_interface_descriptor_t ehci_ifcd = { sizeof(usb_interface_descriptor_t), UDESC_INTERFACE, 0, 0, 1, UICLASS_HUB, UISUBCLASS_HUB, UIPROTO_HSHUBSTT, 0 }; static const usb_endpoint_descriptor_t ehci_endpd = { sizeof(usb_endpoint_descriptor_t), UDESC_ENDPOINT, UE_DIR_IN | EHCI_INTR_ENDPT, UE_INTERRUPT, {8, 0}, /* max packet */ 255 }; static const usb_hub_descriptor_t ehci_hubd = { 0, /* dynamic length */ UDESC_HUB, 0, {0,0}, 0, 0, {0}, }; static void ehci_disown(ehci_softc_t *sc, int index, int lowspeed) { u_int32_t port; u_int32_t v; DPRINTF(("index=%d lowspeed=%d\n", index, lowspeed)); port = EHCI_PORTSC(index); v = EOREAD4(sc, port) &~ EHCI_PS_CLEAR; EOWRITE4(sc, port, v | EHCI_PS_PO); } static void ehci_root_ctrl_enter(struct usbd_xfer *xfer) { ehci_softc_t *sc = xfer->usb_sc; u_int32_t port; u_int32_t v; u_int16_t i; u_int16_t len; u_int16_t value; u_int16_t index; u_int16_t l; u_int16_t totlen = 0; union { usb_status_t stat; usb_port_status_t ps; usb_device_request_t req; usb_hub_descriptor_t hubd; usb_device_descriptor_t devd; usb_device_qualifier_t odevd; usb_config_descriptor_t confd; usb_interface_descriptor_t ifcd; usb_endpoint_descriptor_t endpd; u_int8_t str_temp[128]; u_int8_t byte_temp; } u; usbd_status err; mtx_assert(&sc->sc_bus.mtx, MA_OWNED); if (xfer->length < sizeof(u.req)) { err = USBD_INVAL; goto done; } /* set default actual length */ xfer->actlen = sizeof(u.req); /* copy out "request" */ usbd_copy_out(&(xfer->buf_data), 0, &u.req, sizeof(u.req)); len = (xfer->length - sizeof(u.req)); if (len != UGETW(u.req.wLength)) { err = USBD_INVAL; goto done; } value = UGETW(u.req.wValue); index = UGETW(u.req.wIndex); DPRINTFN(2,("type=0x%02x request=0x%02x wLen=0x%04x " "wValue=0x%04x wIndex=0x%04x\n", u.req.bmRequestType, u.req.bRequest, len, value, index)); #define C(x,y) ((x) | ((y) << 8)) switch(C(u.req.bRequest, u.req.bmRequestType)) { case C(UR_CLEAR_FEATURE, UT_WRITE_DEVICE): case C(UR_CLEAR_FEATURE, UT_WRITE_INTERFACE): case C(UR_CLEAR_FEATURE, UT_WRITE_ENDPOINT): /* * DEVICE_REMOTE_WAKEUP and ENDPOINT_HALT are no-ops * for the integrated root hub. */ break; case C(UR_GET_CONFIG, UT_READ_DEVICE): if(len > 0) { u.byte_temp = sc->sc_conf; totlen = 1; usbd_copy_in(&(xfer->buf_data), sizeof(u.req), &u, totlen); } break; case C(UR_GET_DESCRIPTOR, UT_READ_DEVICE): switch(value >> 8) { case UDESC_DEVICE: if((value & 0xff) != 0) { err = USBD_IOERROR; goto done; } totlen = min(len, sizeof(u.devd)); u.devd = ehci_devd; #if 0 USETW(u.devd.idVendor, sc->sc_id_vendor); #endif usbd_copy_in(&(xfer->buf_data), sizeof(u.req), &u, totlen); break; /* * We can't really operate at another speed, * but the specification says we need this * descriptor: */ case UDESC_DEVICE_QUALIFIER: if((value & 0xff) != 0) { err = USBD_IOERROR; goto done; } totlen = min(len, sizeof(ehci_odevd)); usbd_copy_in(&(xfer->buf_data), sizeof(u.req), &ehci_odevd, totlen); break; /* * We can't really operate at another speed, * but the specification says we need this * descriptor: */ case UDESC_OTHER_SPEED_CONFIGURATION: case UDESC_CONFIG: if((value & 0xff) != 0) { err = USBD_IOERROR; goto done; } totlen = l = min(len, sizeof(u.confd)); len -= l; u.confd = ehci_confd; u.confd.bDescriptorType = (value >> 8); usbd_copy_in(&(xfer->buf_data), sizeof(u.req), &u, l); l = min(len, sizeof(ehci_ifcd)); totlen += l; len -= l; usbd_copy_in(&(xfer->buf_data), sizeof(u.req) + sizeof(u.confd), &ehci_ifcd, l); l = min(len, sizeof(ehci_endpd)); totlen += l; len -= l; usbd_copy_in(&(xfer->buf_data), sizeof(u.req) + sizeof(u.confd) + sizeof(u.ifcd), &ehci_endpd, l); break; case UDESC_STRING: if(len == 0) { break; } switch (value & 0xff) { case 0: /* Language table */ totlen = usbd_make_str_desc (u.str_temp, sizeof(u.str_temp), "\001"); break; case 1: /* Vendor */ totlen = usbd_make_str_desc (u.str_temp, sizeof(u.str_temp), sc->sc_vendor); break; case 2: /* Product */ totlen = usbd_make_str_desc (u.str_temp, sizeof(u.str_temp), "EHCI root hub"); break; default: totlen = usbd_make_str_desc (u.str_temp, sizeof(u.str_temp), ""); break; } if (totlen > len) { totlen = len; } usbd_copy_in(&(xfer->buf_data), sizeof(u.req), &u, totlen); break; default: err = USBD_IOERROR; goto done; } break; case C(UR_GET_INTERFACE, UT_READ_INTERFACE): if(len > 0) { u.byte_temp = 0; totlen = 1; usbd_copy_in(&(xfer->buf_data), sizeof(u.req), &u, totlen); } break; case C(UR_GET_STATUS, UT_READ_DEVICE): if(len > 1) { USETW(u.stat.wStatus,UDS_SELF_POWERED); totlen = 2; usbd_copy_in(&(xfer->buf_data), sizeof(u.req), &u, totlen); } break; case C(UR_GET_STATUS, UT_READ_INTERFACE): case C(UR_GET_STATUS, UT_READ_ENDPOINT): if(len > 1) { USETW(u.stat.wStatus, 0); totlen = 2; usbd_copy_in(&(xfer->buf_data), sizeof(u.req), &u, totlen); } break; case C(UR_SET_ADDRESS, UT_WRITE_DEVICE): if(value >= USB_MAX_DEVICES) { err = USBD_IOERROR; goto done; } sc->sc_addr = value; break; case C(UR_SET_CONFIG, UT_WRITE_DEVICE): if((value != 0) && (value != 1)) { err = USBD_IOERROR; goto done; } sc->sc_conf = value; break; case C(UR_SET_DESCRIPTOR, UT_WRITE_DEVICE): break; case C(UR_SET_FEATURE, UT_WRITE_DEVICE): case C(UR_SET_FEATURE, UT_WRITE_INTERFACE): case C(UR_SET_FEATURE, UT_WRITE_ENDPOINT): err = USBD_IOERROR; goto done; case C(UR_SET_INTERFACE, UT_WRITE_INTERFACE): break; case C(UR_SYNCH_FRAME, UT_WRITE_ENDPOINT): break; /* Hub requests */ case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_DEVICE): break; case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_OTHER): DPRINTFN(8, ("UR_CLEAR_PORT_FEATURE\n")); if((index < 1) || (index > sc->sc_noport)) { err = USBD_IOERROR; goto done; } port = EHCI_PORTSC(index); v = EOREAD4(sc, port) &~ EHCI_PS_CLEAR; switch(value) { case UHF_PORT_ENABLE: EOWRITE4(sc, port, v &~ EHCI_PS_PE); break; case UHF_PORT_SUSPEND: EOWRITE4(sc, port, v &~ EHCI_PS_SUSP); break; case UHF_PORT_POWER: EOWRITE4(sc, port, v &~ EHCI_PS_PP); break; case UHF_PORT_TEST: DPRINTFN(2,("clear port test " "%d\n", index)); break; case UHF_PORT_INDICATOR: DPRINTFN(2,("clear port ind " "%d\n", index)); EOWRITE4(sc, port, v &~ EHCI_PS_PIC); break; case UHF_C_PORT_CONNECTION: EOWRITE4(sc, port, v | EHCI_PS_CSC); break; case UHF_C_PORT_ENABLE: EOWRITE4(sc, port, v | EHCI_PS_PEC); break; case UHF_C_PORT_SUSPEND: /* how? */ break; case UHF_C_PORT_OVER_CURRENT: EOWRITE4(sc, port, v | EHCI_PS_OCC); break; case UHF_C_PORT_RESET: sc->sc_isreset = 0; break; default: err = USBD_IOERROR; goto done; } #if 0 switch(value) { case UHF_C_PORT_CONNECTION: case UHF_C_PORT_ENABLE: case UHF_C_PORT_SUSPEND: case UHF_C_PORT_OVER_CURRENT: case UHF_C_PORT_RESET: /* Enable RHSC interrupt if condition is cleared. */ if((OREAD4(sc, port) >> 16) == 0) { mtx_lock(&sc->sc_bus.mtx); ehci_pcd_enable(sc); } break; default: break; } #endif break; case C(UR_GET_DESCRIPTOR, UT_READ_CLASS_DEVICE): if((value & 0xff) != 0) { err = USBD_IOERROR; goto done; } v = EOREAD4(sc, EHCI_HCSPARAMS); u.hubd = ehci_hubd; u.hubd.bNbrPorts = sc->sc_noport; USETW(u.hubd.wHubCharacteristics, (EHCI_HCS_PPC(v) ? UHD_PWR_INDIVIDUAL : UHD_PWR_NO_SWITCH) | (EHCI_HCS_P_INDICATOR(EREAD4(sc, EHCI_HCSPARAMS)) ? UHD_PORT_IND : 0)); u.hubd.bPwrOn2PwrGood = 200; /* XXX can't find out? */ for(l = 0; l < sc->sc_noport; l++) { /* XXX can't find out? */ u.hubd.DeviceRemovable[l/8] &= ~(1 << (l % 8)); } u.hubd.bDescLength = (USB_HUB_DESCRIPTOR_SIZE-1) + ((sc->sc_noport + 7)/8); totlen = min(len, u.hubd.bDescLength); usbd_copy_in(&(xfer->buf_data), sizeof(u.req), &u, totlen); break; case C(UR_GET_STATUS, UT_READ_CLASS_DEVICE): if(len < 4) { err = USBD_IOERROR; goto done; } usbd_bzero(&(xfer->buf_data), sizeof(u.req), len); totlen = len; break; case C(UR_GET_STATUS, UT_READ_CLASS_OTHER): DPRINTFN(8,("get port status i=%d\n", index)); if((index < 1) || (index > sc->sc_noport)) { err = USBD_IOERROR; goto done; } if(len < 4) { err = USBD_IOERROR; goto done; } v = EOREAD4(sc, EHCI_PORTSC(index)); DPRINTFN(8,("port status=0x%04x\n", v)); i = UPS_HIGH_SPEED; if(v & EHCI_PS_CS) i |= UPS_CURRENT_CONNECT_STATUS; if(v & EHCI_PS_PE) i |= UPS_PORT_ENABLED; if(v & EHCI_PS_SUSP) i |= UPS_SUSPEND; if(v & EHCI_PS_OCA) i |= UPS_OVERCURRENT_INDICATOR; if(v & EHCI_PS_PR) i |= UPS_RESET; if(v & EHCI_PS_PP) i |= UPS_PORT_POWER; USETW(u.ps.wPortStatus, i); i = 0; if(v & EHCI_PS_CSC) i |= UPS_C_CONNECT_STATUS; if(v & EHCI_PS_PEC) i |= UPS_C_PORT_ENABLED; if(v & EHCI_PS_OCC) i |= UPS_C_OVERCURRENT_INDICATOR; if(sc->sc_isreset) i |= UPS_C_PORT_RESET; USETW(u.ps.wPortChange, i); totlen = min(len, sizeof(u.ps)); usbd_copy_in(&(xfer->buf_data), sizeof(u.req), &u, totlen); break; case C(UR_SET_DESCRIPTOR, UT_WRITE_CLASS_DEVICE): err = USBD_IOERROR; goto done; case C(UR_SET_FEATURE, UT_WRITE_CLASS_DEVICE): break; case C(UR_SET_FEATURE, UT_WRITE_CLASS_OTHER): if((index < 1) || (index > sc->sc_noport)) { err = USBD_IOERROR; goto done; } port = EHCI_PORTSC(index); v = EOREAD4(sc, port) &~ EHCI_PS_CLEAR; switch(value) { case UHF_PORT_ENABLE: EOWRITE4(sc, port, v | EHCI_PS_PE); break; case UHF_PORT_SUSPEND: EOWRITE4(sc, port, v | EHCI_PS_SUSP); break; case UHF_PORT_RESET: DPRINTFN(5,("reset port %d\n", index)); if(EHCI_PS_IS_LOWSPEED(v)) { /* Low speed device, give up ownership. */ ehci_disown(sc, index, 1); break; } /* Start reset sequence. */ v &= ~ (EHCI_PS_PE | EHCI_PS_PR); EOWRITE4(sc, port, v | EHCI_PS_PR); /* Wait for reset to complete. */ DELAY(1000*USB_PORT_ROOT_RESET_DELAY); /* Terminate reset sequence. */ EOWRITE4(sc, port, v); /* Wait for HC to complete reset. */ DELAY(1000*EHCI_PORT_RESET_COMPLETE); v = EOREAD4(sc, port); DPRINTF(("ehci after reset, status=0x%08x\n", v)); if(v & EHCI_PS_PR) { device_printf(sc->sc_bus.bdev, "port reset timeout\n"); err = USBD_TIMEOUT; goto done; } if(!(v & EHCI_PS_PE)) { /* Not a high speed device, * give up ownership. */ ehci_disown(sc, index, 0); break; } sc->sc_isreset = 1; DPRINTF(("ehci port %d reset, status = 0x%08x\n", index, v)); break; case UHF_PORT_POWER: DPRINTFN(2,("set port power %d\n", index)); EOWRITE4(sc, port, v | EHCI_PS_PP); break; case UHF_PORT_TEST: DPRINTFN(2,("set port test %d\n", index)); break; case UHF_PORT_INDICATOR: DPRINTFN(2,("set port ind %d\n", index)); EOWRITE4(sc, port, v | EHCI_PS_PIC); break; default: err = USBD_IOERROR; goto done; } break; case C(UR_CLEAR_TT_BUFFER, UT_WRITE_CLASS_OTHER): case C(UR_RESET_TT, UT_WRITE_CLASS_OTHER): case C(UR_GET_TT_STATE, UT_READ_CLASS_OTHER): case C(UR_STOP_TT, UT_WRITE_CLASS_OTHER): break; default: err = USBD_IOERROR; goto done; } xfer->actlen = totlen + sizeof(u.req); err = USBD_NORMAL_COMPLETION; done: /* transfer transferred */ ehci_device_done(xfer, err); /* call callback recursively */ __usbd_callback(xfer); return; } static void ehci_root_ctrl_start(struct usbd_xfer *xfer) { /* not used */ return; } struct usbd_pipe_methods ehci_root_ctrl_methods = { .open = ehci_root_ctrl_open, .close = ehci_root_ctrl_close, .enter = ehci_root_ctrl_enter, .start = ehci_root_ctrl_start, .copy_in = usbd_std_ctrl_copy_in, .copy_out = usbd_std_ctrl_copy_out, }; /*---------------------------------------------------------------------------* * ehci root interrupt support *---------------------------------------------------------------------------*/ static void ehci_root_intr_open(struct usbd_xfer *xfer) { return; } static void ehci_root_intr_close(struct usbd_xfer *xfer) { ehci_device_done(xfer, USBD_CANCELLED); return; } static void ehci_root_intr_enter(struct usbd_xfer *xfer) { DPRINTFN(3, ("xfer=%p len=%d\n", xfer, xfer->length)); /* enqueue transfer * (so that it can be aborted through pipe abort) */ usbd_transfer_enqueue(xfer); return; } static void ehci_root_intr_start(struct usbd_xfer *xfer) { ehci_softc_t *sc = xfer->usb_sc; /* only one transfer at at time * (sc_intrxfer is cleared by ehci_root_intr_done()) */ sc->sc_intrxfer = xfer; return; } struct usbd_pipe_methods ehci_root_intr_methods = { .open = ehci_root_intr_open, .close = ehci_root_intr_close, .enter = ehci_root_intr_enter, .start = ehci_root_intr_start, .copy_in = usbd_std_bulk_intr_copy_in, .copy_out = usbd_std_bulk_intr_copy_out, }; #define ADD_BYTES(ptr,size) ((void *)(((u_int8_t *)(ptr)) + (size))) static usbd_status ehci_xfer_setup(struct usbd_device *udev, u_int8_t iface_index, struct usbd_xfer **pxfer, const struct usbd_config *setup_start, const struct usbd_config *setup_end) { ehci_softc_t *sc = EHCI_BUS2SC(udev->bus); const struct usbd_config *setup; struct usbd_memory_info *info; struct usbd_page *page_ptr; struct usbd_xfer dummy; struct usbd_xfer *xfer; u_int32_t size[2]; u_int32_t total_size[2]; u_int32_t nqtd; u_int32_t nqh; u_int32_t nsitd; u_int32_t nitd; u_int32_t n; void *buf; void *temp_ptr; void *last_obj; bus_size_t temp_phy; usbd_status error = 0; buf = NULL; page_ptr = NULL; total_size[0] = 0; total_size[1] = 0; repeat: size[0] = 0; size[1] = 0; /* align data to 8 byte boundary */ size[0] += ((-size[0]) & (USB_HOST_ALIGN-1)); if(buf) { info = ADD_BYTES(buf,size[0]); info->memory_base = buf; info->memory_size = total_size[0]; info->page_base = page_ptr; info->page_size = total_size[1]; info->usb_mtx = &sc->sc_bus.mtx; } else { info = NULL; } size[0] += sizeof(info[0]); for(setup = setup_start; setup < setup_end; setup++) { nqtd = 0; nqh = 0; nsitd = 0; nitd = 0; /* align data to 8 byte boundary */ size[0] += ((-size[0]) & (USB_HOST_ALIGN-1)); if(buf) { xfer = ADD_BYTES(buf,size[0]); *pxfer++ = xfer; } else { /* need dummy xfer to * calculate nqtd and nqh ! */ xfer = &dummy; bzero(&dummy, sizeof(dummy)); } /* * setup xfer */ xfer->usb_sc = sc; xfer->usb_mtx = &sc->sc_bus.mtx; xfer->usb_root = info; xfer->address = udev->address; xfer->pipe = usbd_get_pipe(udev, iface_index, setup); if(!xfer->pipe) { error = USBD_NO_PIPE; DPRINTF(("no pipe for endpoint %d\n", setup->endpoint)); goto done; } else { /* * compute maximum number of some structures */ if((xfer->pipe->methods == &ehci_device_ctrl_methods) || (xfer->pipe->methods == &ehci_device_bulk_methods) || (xfer->pipe->methods == &ehci_device_intr_methods)) { usbd_std_transfer_setup(xfer, setup, 0x400, 0x400); nqh = 1; nqtd = (1+ /* SETUP */ 1+ /* STATUS */ 1 /* SHORTPKT */) + (xfer->length / (EHCI_PAGE_SIZE/2)); /* DATA */ } else if(xfer->pipe->methods == &ehci_device_isoc_fs_methods) { usbd_std_transfer_setup(xfer, setup, 188, 188); if(xfer->nframes == 0) { error = USBD_ZERO_FRAMES_IN_ISOC_MODE; DPRINTF(("frames == 0 in isochronous mode; " "endpoint 0x%02x\n", setup->endpoint)); goto done; } if(xfer->nframes >= EHCI_VIRTUAL_FRAMELIST_COUNT) { error = USBD_INVAL; DPRINTF(("isochronous frame-limit " "exceeded by 0x%x frames; " "endpoint 0x%02x\n", setup->frames - EHCI_VIRTUAL_FRAMELIST_COUNT, setup->endpoint)); goto done; } nsitd = xfer->nframes; } else if(xfer->pipe->methods == &ehci_device_isoc_hs_methods) { usbd_std_transfer_setup(xfer, setup, 0x400, 0xC00); if(xfer->nframes == 0) { error = USBD_ZERO_FRAMES_IN_ISOC_MODE; DPRINTF(("frames == 0 in isochronous mode; " "endpoint 0x%02x\n", setup->endpoint)); goto done; } if(xfer->nframes >= (8*EHCI_VIRTUAL_FRAMELIST_COUNT)) { error = USBD_INVAL; DPRINTF(("isochronous frame-limit " "exceeded by 0x%x frames; " "endpoint 0x%02x\n", setup->frames - (8*EHCI_VIRTUAL_FRAMELIST_COUNT), setup->endpoint)); goto done; } nitd = (xfer->nframes + 7) / 8; } else { usbd_std_transfer_setup(xfer, setup, 0x400, 0x400); } } size[0] += sizeof(xfer[0]); if(xfer->nframes) { /* align data to 8 byte boundary */ size[0] += ((-size[0]) & (USB_HOST_ALIGN-1)); xfer->frlengths = ADD_BYTES(buf,size[0]); size[0] += (xfer->nframes * sizeof(xfer->frlengths[0])); xfer->frlengths_old = ADD_BYTES(buf,size[0]); size[0] += (xfer->nframes * sizeof(xfer->frlengths[0])); } if (!(xfer->flags & USBD_USE_DMA)) { /* align data to 8 byte boundary */ size[0] += ((-size[0]) & (USB_HOST_ALIGN-1)); xfer->buffer = ADD_BYTES(buf,size[0]); size[0] += xfer->length; } /*****/ /* align data to 8 byte boundary */ size[1] += ((-size[1]) & (USB_HOST_ALIGN-1)); usbd_page_set_start(&(xfer->buf_data), page_ptr, size[1]); size[1] += xfer->length; usbd_page_set_end(&(xfer->buf_data), page_ptr, size[1]); /* memory is allocated at * highest alignment * which is first */ if(nitd) { /* align data */ size[1] += ((-size[1]) & (EHCI_ITD_ALIGN-1)); } if(nsitd) { /* align data */ size[1] += ((-size[1]) & (EHCI_SITD_ALIGN-1)); } if(nqtd) { /* align data */ size[1] += ((-size[1]) & (EHCI_QTD_ALIGN-1)); } last_obj = NULL; for(n = 0; n < nitd; n++) { size[1] += usbd_page_fit_obj(page_ptr, size[1], sizeof(ehci_itd_t)); if(buf) { temp_ptr = usbd_page_get_buf(page_ptr,size[1]); temp_phy = usbd_page_get_phy(page_ptr,size[1]); /* init TD */ ((ehci_itd_t *)temp_ptr)->itd_self = htole32(temp_phy|EHCI_LINK_ITD); ((ehci_itd_t *)temp_ptr)->obj_next = last_obj; last_obj = temp_ptr; } size[1] += sizeof(ehci_itd_t); } for(n = 0; n < nsitd; n++) { size[1] += usbd_page_fit_obj(page_ptr, size[1], sizeof(ehci_sitd_t)); if(buf) { temp_ptr = usbd_page_get_buf(page_ptr,size[1]); temp_phy = usbd_page_get_phy(page_ptr,size[1]); /* init TD */ ((ehci_sitd_t *)temp_ptr)->sitd_self = htole32(temp_phy|EHCI_LINK_SITD); ((ehci_sitd_t *)temp_ptr)->obj_next = last_obj; last_obj = temp_ptr; } size[1] += sizeof(ehci_sitd_t); } for(n = 0; n < nqtd; n++) { size[1] += usbd_page_fit_obj(page_ptr, size[1], sizeof(ehci_qtd_t)); if(buf) { temp_ptr = usbd_page_get_buf(page_ptr,size[1]); temp_phy = usbd_page_get_phy(page_ptr,size[1]); /* init TD */ ((ehci_qtd_t *)temp_ptr)->qtd_self = htole32(temp_phy); ((ehci_qtd_t *)temp_ptr)->obj_next = last_obj; last_obj = temp_ptr; } size[1] += sizeof(ehci_qtd_t); } xfer->td_start = last_obj; if(nqh) { /* align data */ size[1] += ((-size[1]) & (EHCI_QH_ALIGN-1)); } last_obj = NULL; for(n = 0; n < nqh; n++) { size[1] += usbd_page_fit_obj(page_ptr, size[1], sizeof(ehci_qh_t)); if(buf) { temp_ptr = usbd_page_get_buf(page_ptr,size[1]); temp_phy = usbd_page_get_phy(page_ptr,size[1]); /* init QH */ ((ehci_qh_t *)temp_ptr)->qh_self = htole32(temp_phy|EHCI_LINK_QH); ((ehci_qh_t *)temp_ptr)->obj_next = last_obj; last_obj = temp_ptr; } size[1] += sizeof(ehci_qh_t); } xfer->qh_start = last_obj; } if(buf || error) { goto done; } /* compute number of USB pages required */ total_size[1] = (size[1] + USB_PAGE_SIZE - 1) / USB_PAGE_SIZE; /* align data to 8 byte boundary */ size[0] += ((-size[0]) & (USB_HOST_ALIGN-1)); /* store offset temporarily */ n = size[0]; size[0] += (sizeof(*page_ptr) * total_size[1]); /* store total buffer size */ total_size[0] = size[0]; /* allocate zeroed memory */ buf = malloc(total_size[0], M_USB, M_WAITOK|M_ZERO); if(buf == NULL) { error = USBD_NOMEM; DPRINTF(("cannot allocate memory block for " "configuration (%d bytes)\n", total_size[0])); goto done; } page_ptr = ADD_BYTES(buf,n); if (usbd_page_alloc(sc->sc_bus.dma_tag, page_ptr, total_size[1])) { free(buf, M_USB); error = USBD_NOMEM; DPRINTF(("cannot allocate memory block for " "configuration (%d USB pages)\n", total_size[1])); goto done; } goto repeat; done: return error; } static void ehci_pipe_init(struct usbd_device *udev, usb_endpoint_descriptor_t *edesc, struct usbd_pipe *pipe) { ehci_softc_t *sc = EHCI_BUS2SC(udev->bus); DPRINTFN(1, ("pipe=%p, addr=%d, endpt=%d (%d)\n", pipe, udev->address, edesc->bEndpointAddress, sc->sc_addr)); if(udev->address == sc->sc_addr) { switch (edesc->bEndpointAddress) { case USB_CONTROL_ENDPOINT: pipe->methods = &ehci_root_ctrl_methods; break; case UE_DIR_IN | EHCI_INTR_ENDPT: pipe->methods = &ehci_root_intr_methods; break; default: panic("invalid endpoint address: 0x%02x", edesc->bEndpointAddress); break; } } else { switch (edesc->bmAttributes & UE_XFERTYPE) { case UE_CONTROL: pipe->methods = &ehci_device_ctrl_methods; break; case UE_INTERRUPT: pipe->methods = &ehci_device_intr_methods; break; case UE_ISOCHRONOUS: if(udev->speed == USB_SPEED_HIGH) { pipe->methods = &ehci_device_isoc_hs_methods; } else { pipe->methods = &ehci_device_isoc_fs_methods; } break; case UE_BULK: pipe->methods = &ehci_device_bulk_methods; break; } } return; } struct usbd_bus_methods ehci_bus_methods = { .pipe_init = ehci_pipe_init, .xfer_setup = ehci_xfer_setup, .do_poll = ehci_do_poll, }; pwcbsd/usb/ehci.h000644 000423 000000 00000044426 10551670753 014473 0ustar00luigiwheel000000 000000 /*- * Copyright (c) 2001 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net). * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifndef _EHCI_H_ #define _EHCI_H_ /* PCI config registers */ #define PCI_CBMEM 0x10 /* configuration base MEM */ #define PCI_INTERFACE_EHCI 0x20 #define PCI_USBREV 0x60 /* RO USB protocol revision */ #define PCI_USBREV_MASK 0xff #define PCI_USBREV_PRE_1_0 0x00 #define PCI_USBREV_1_0 0x10 #define PCI_USBREV_1_1 0x11 #define PCI_USBREV_2_0 0x20 #define PCI_EHCI_FLADJ 0x61 /* RW Frame len adj, SOF=59488+6*fladj */ #define PCI_EHCI_PORTWAKECAP 0x62 /* RW Port wake caps (opt) */ /* EHCI Extended Capabilities */ #define EHCI_EC_LEGSUP 0x01 #define EHCI_EECP_NEXT(x) (((x) >> 8) & 0xff) #define EHCI_EECP_ID(x) ((x) & 0xff) /* Legacy support extended capability */ #define EHCI_LEGSUP_LEGSUP 0x01 #define EHCI_LEGSUP_OSOWNED 0x01000000 /* OS owned semaphore */ #define EHCI_LEGSUP_BIOSOWNED 0x00010000 /* BIOS owned semaphore */ #define EHCI_LEGSUP_USBLEGCTLSTS 0x04 /* EHCI capability registers */ #define EHCI_CAPLENGTH 0x00 /* RO Capability register length field */ /* reserved 0x01 */ #define EHCI_HCIVERSION 0x02 /* RO Interface version number */ #define EHCI_HCSPARAMS 0x04 /* RO Structural parameters */ #define EHCI_HCS_DEBUGPORT(x) (((x) >> 20) & 0xf) #define EHCI_HCS_P_INDICATOR(x) ((x) & 0x10000) #define EHCI_HCS_N_CC(x) (((x) >> 12) & 0xf) /* # of companion ctlrs */ #define EHCI_HCS_N_PCC(x) (((x) >> 8) & 0xf) /* # of ports per comp. */ #define EHCI_HCS_PPC(x) ((x) & 0x10) /* port power control */ #define EHCI_HCS_N_PORTS(x) ((x) & 0xf) /* # of ports */ #define EHCI_HCCPARAMS 0x08 /* RO Capability parameters */ #define EHCI_HCC_EECP(x) (((x) >> 8) & 0xff) /* extended ports caps */ #define EHCI_HCC_IST(x) (((x) >> 4) & 0xf) /* isoc sched threshold */ #define EHCI_HCC_ASPC(x) ((x) & 0x4) /* async sched park cap */ #define EHCI_HCC_PFLF(x) ((x) & 0x2) /* prog frame list flag */ #define EHCI_HCC_64BIT(x) ((x) & 0x1) /* 64 bit address cap */ #define EHCI_HCSP_PORTROUTE 0x0c /* RO Companion port route description */ /* EHCI operational registers. Offset given by EHCI_CAPLENGTH register */ #define EHCI_USBCMD 0x00 /* RO, RW, WO Command register */ #define EHCI_CMD_ITC_M 0x00ff0000 /* RW interrupt threshold ctrl */ #define EHCI_CMD_ITC_1 0x00010000 #define EHCI_CMD_ITC_2 0x00020000 #define EHCI_CMD_ITC_4 0x00040000 #define EHCI_CMD_ITC_8 0x00080000 #define EHCI_CMD_ITC_16 0x00100000 #define EHCI_CMD_ITC_32 0x00200000 #define EHCI_CMD_ITC_64 0x00400000 #define EHCI_CMD_ASPME 0x00000800 /* RW/RO async park enable */ #define EHCI_CMD_ASPMC 0x00000300 /* RW/RO async park count */ #define EHCI_CMD_LHCR 0x00000080 /* RW light host ctrl reset */ #define EHCI_CMD_IAAD 0x00000040 /* RW intr on async adv door bell */ #define EHCI_CMD_ASE 0x00000020 /* RW async sched enable */ #define EHCI_CMD_PSE 0x00000010 /* RW periodic sched enable */ #define EHCI_CMD_FLS_M 0x0000000c /* RW/RO frame list size */ #define EHCI_CMD_FLS(x) (((x) >> 2) & 3) /* RW/RO frame list size */ #define EHCI_CMD_HCRESET 0x00000002 /* RW reset */ #define EHCI_CMD_RS 0x00000001 /* RW run/stop */ #define EHCI_USBSTS 0x04 /* RO, RW, RWC Status register */ #define EHCI_STS_ASS 0x00008000 /* RO async sched status */ #define EHCI_STS_PSS 0x00004000 /* RO periodic sched status */ #define EHCI_STS_REC 0x00002000 /* RO reclamation */ #define EHCI_STS_HCH 0x00001000 /* RO host controller halted */ #define EHCI_STS_IAA 0x00000020 /* RWC interrupt on async adv */ #define EHCI_STS_HSE 0x00000010 /* RWC host system error */ #define EHCI_STS_FLR 0x00000008 /* RWC frame list rollover */ #define EHCI_STS_PCD 0x00000004 /* RWC port change detect */ #define EHCI_STS_ERRINT 0x00000002 /* RWC error interrupt */ #define EHCI_STS_INT 0x00000001 /* RWC interrupt */ #define EHCI_STS_INTRS(x) ((x) & 0x3f) #define EHCI_NORMAL_INTRS (/* EHCI_STS_IAA | */ EHCI_STS_HSE | \ EHCI_STS_PCD | EHCI_STS_ERRINT | EHCI_STS_INT) #define EHCI_USBINTR 0x08 /* RW Interrupt register */ #define EHCI_INTR_IAAE 0x00000020 /* interrupt on async advance ena */ #define EHCI_INTR_HSEE 0x00000010 /* host system error ena */ #define EHCI_INTR_FLRE 0x00000008 /* frame list rollover ena */ #define EHCI_INTR_PCIE 0x00000004 /* port change ena */ #define EHCI_INTR_UEIE 0x00000002 /* USB error intr ena */ #define EHCI_INTR_UIE 0x00000001 /* USB intr ena */ #define EHCI_FRINDEX 0x0c /* RW Frame Index register */ #define EHCI_CTRLDSSEGMENT 0x10 /* RW Control Data Structure Segment */ #define EHCI_PERIODICLISTBASE 0x14 /* RW Periodic List Base */ #define EHCI_ASYNCLISTADDR 0x18 /* RW Async List Base */ #define EHCI_CONFIGFLAG 0x40 /* RW Configure Flag register */ #define EHCI_CONF_CF 0x00000001 /* RW configure flag */ #define EHCI_PORTSC(n) (0x40+(4*(n))) /* RO, RW, RWC Port Status reg */ #define EHCI_PS_WKOC_E 0x00400000 /* RW wake on over current ena */ #define EHCI_PS_WKDSCNNT_E 0x00200000 /* RW wake on disconnect ena */ #define EHCI_PS_WKCNNT_E 0x00100000 /* RW wake on connect ena */ #define EHCI_PS_PTC 0x000f0000 /* RW port test control */ #define EHCI_PS_PIC 0x0000c000 /* RW port indicator control */ #define EHCI_PS_PO 0x00002000 /* RW port owner */ #define EHCI_PS_PP 0x00001000 /* RW,RO port power */ #define EHCI_PS_LS 0x00000c00 /* RO line status */ #define EHCI_PS_IS_LOWSPEED(x) (((x) & EHCI_PS_LS) == 0x00000400) #define EHCI_PS_PR 0x00000100 /* RW port reset */ #define EHCI_PS_SUSP 0x00000080 /* RW suspend */ #define EHCI_PS_FPR 0x00000040 /* RW force port resume */ #define EHCI_PS_OCC 0x00000020 /* RWC over current change */ #define EHCI_PS_OCA 0x00000010 /* RO over current active */ #define EHCI_PS_PEC 0x00000008 /* RWC port enable change */ #define EHCI_PS_PE 0x00000004 /* RW port enable */ #define EHCI_PS_CSC 0x00000002 /* RWC connect status change */ #define EHCI_PS_CS 0x00000001 /* RO connect status */ #define EHCI_PS_CLEAR (EHCI_PS_OCC | EHCI_PS_PEC | EHCI_PS_CSC) #define EHCI_PORT_RESET_COMPLETE 2 /* ms */ /* * Alignment NOTE: structures must be aligned so that the hardware can index * without performing addition. */ #define EHCI_FRAMELIST_ALIGN 0x1000 /* bytes */ #define EHCI_FRAMELIST_COUNT 1024 /* units */ #define EHCI_VIRTUAL_FRAMELIST_COUNT 128 /* units */ /* Link types */ #define EHCI_LINK_TERMINATE 0x00000001 #define EHCI_LINK_TYPE(x) ((x) & 0x00000006) #define EHCI_LINK_ITD 0x0 #define EHCI_LINK_QH 0x2 #define EHCI_LINK_SITD 0x4 #define EHCI_LINK_FSTN 0x6 #define EHCI_LINK_ADDR(x) ((x) &~ 0x1f) /* Structures alignment (bytes) */ #define EHCI_ITD_ALIGN 128 #define EHCI_SITD_ALIGN 64 #define EHCI_QTD_ALIGN 64 #define EHCI_QH_ALIGN 128 #define EHCI_FSTN_ALIGN 32 /* Data buffers are divided into one or more pages */ #define EHCI_PAGE_SIZE 0x1000 #if ((USB_PAGE_SIZE < EHCI_PAGE_SIZE) || (EHCI_PAGE_SIZE == 0) || \ (USB_PAGE_SIZE < EHCI_ITD_ALIGN) || (EHCI_ITD_ALIGN == 0) || \ (USB_PAGE_SIZE < EHCI_SITD_ALIGN) || (EHCI_SITD_ALIGN == 0) || \ (USB_PAGE_SIZE < EHCI_QTD_ALIGN) || (EHCI_QTD_ALIGN == 0) || \ (USB_PAGE_SIZE < EHCI_QH_ALIGN) || (EHCI_QH_ALIGN == 0) || \ (USB_PAGE_SIZE < EHCI_FSTN_ALIGN) || (EHCI_FSTN_ALIGN == 0)) #error "Invalid USB page size!" #endif /* * Isochronous Transfer Descriptor. This descriptor is used for high speed * transfers only. */ typedef struct ehci_itd { volatile uint32_t itd_next; volatile uint32_t itd_status[8]; #define EHCI_ITD_SET_LEN(x) ((x) << 16) #define EHCI_ITD_GET_LEN(x) (((x) >> 16) & 0xFFF) #define EHCI_ITD_IOC (1 << 15) #define EHCI_ITD_SET_PG(x) ((x) << 12) #define EHCI_ITD_GET_PG(x) (((x) >> 12) & 0x7) #define EHCI_ITD_SET_OFFS(x) (x) #define EHCI_ITD_GET_OFFS(x) (((x) >> 0) & 0xFFF) #define EHCI_ITD_ACTIVE (1 << 31) #define EHCI_ITD_DATABUFERR (1 << 30) #define EHCI_ITD_BABBLE (1 << 29) #define EHCI_ITD_XACTERR (1 << 28) volatile uint32_t itd_bp[7]; /* itd_bp[0] */ #define EHCI_ITD_SET_ADDR(x) (x) #define EHCI_ITD_GET_ADDR(x) (((x) >> 0) & 0x7F) #define EHCI_ITD_SET_ENDPT(x) ((x) << 8) #define EHCI_ITD_GET_ENDPT(x) (((x) >> 8) & 0xF) /* itd_bp[1] */ #define EHCI_ITD_SET_DIR_IN (1 << 11) #define EHCI_ITD_SET_DIR_OUT (0 << 11) #define EHCI_ITD_SET_MPL(x) (x) #define EHCI_ITD_GET_MPL(x) (((x) >> 0) & 0x7FF) volatile uint32_t itd_bp_hi[7]; /* * Extra information needed: */ uint32_t itd_self; struct ehci_itd *next; struct ehci_itd *prev; struct ehci_itd *obj_next; } __aligned(EHCI_ITD_ALIGN) ehci_itd_t; /* * Split Transaction Isochronous Transfer Descriptor. This descriptor is used * for full speed transfers only. */ typedef struct ehci_sitd { volatile uint32_t sitd_next; volatile uint32_t sitd_portaddr; #define EHCI_SITD_SET_DIR_OUT (0 << 31) #define EHCI_SITD_SET_DIR_IN (1 << 31) #define EHCI_SITD_SET_ADDR(x) (x) #define EHCI_SITD_GET_ADDR(x) ((x) & 0x7F) #define EHCI_SITD_SET_ENDPT(x) ((x) << 8) #define EHCI_SITD_GET_ENDPT(x) (((x) >> 8) & 0xF) #define EHCI_SITD_GET_DIR(x) ((x) >> 31) #define EHCI_SITD_SET_PORT(x) ((x) << 24) #define EHCI_SITD_GET_PORT(x) (((x) >> 24) & 0x7F) #define EHCI_SITD_SET_HUBA(x) ((x) << 16) #define EHCI_SITD_GET_HUBA(x) (((x) >> 16) & 0x7F) volatile uint32_t sitd_mask; #define EHCI_SITD_SET_SMASK(x) (x) #define EHCI_SITD_SET_CMASK(x) ((x) << 8) volatile uint32_t sitd_status; #define EHCI_SITD_COMPLETE_SPLIT (1<<1) #define EHCI_SITD_START_SPLIT (0<<1) #define EHCI_SITD_MISSED_MICRO_FRAME (1<<2) #define EHCI_SITD_XACTERR (1<<3) #define EHCI_SITD_BABBLE (1<<4) #define EHCI_SITD_DATABUFERR (1<<5) #define EHCI_SITD_ERROR (1<<6) #define EHCI_SITD_ACTIVE (1<<7) #define EHCI_SITD_IOC (1<<31) #define EHCI_SITD_SET_LEN(len) ((len)<<16) #define EHCI_SITD_GET_LEN(x) (((x)>>16) & 0x3FF) volatile uint32_t sitd_bp[2]; volatile uint32_t sitd_back; volatile uint32_t sitd_bp_hi[2]; /* * Extra information needed: */ uint32_t sitd_self; struct ehci_sitd *next; struct ehci_sitd *prev; struct ehci_sitd *obj_next; } __aligned(EHCI_SITD_ALIGN) ehci_sitd_t; /* Queue Element Transfer Descriptor */ typedef struct ehci_qtd { volatile uint32_t qtd_next; volatile uint32_t qtd_altnext; volatile uint32_t qtd_status; #define EHCI_QTD_GET_STATUS(x) (((x) >> 0) & 0xff) #define EHCI_QTD_SET_STATUS(x) ((x) << 0) #define EHCI_QTD_ACTIVE 0x80 #define EHCI_QTD_HALTED 0x40 #define EHCI_QTD_BUFERR 0x20 #define EHCI_QTD_BABBLE 0x10 #define EHCI_QTD_XACTERR 0x08 #define EHCI_QTD_MISSEDMICRO 0x04 #define EHCI_QTD_SPLITXSTATE 0x02 #define EHCI_QTD_PINGSTATE 0x01 #define EHCI_QTD_STATERRS 0x74 #define EHCI_QTD_GET_PID(x) (((x) >> 8) & 0x3) #define EHCI_QTD_SET_PID(x) ((x) << 8) #define EHCI_QTD_PID_OUT 0x0 #define EHCI_QTD_PID_IN 0x1 #define EHCI_QTD_PID_SETUP 0x2 #define EHCI_QTD_GET_CERR(x) (((x) >> 10) & 0x3) #define EHCI_QTD_SET_CERR(x) ((x) << 10) #define EHCI_QTD_GET_C_PAGE(x) (((x) >> 12) & 0x7) #define EHCI_QTD_SET_C_PAGE(x) ((x) << 12) #define EHCI_QTD_GET_IOC(x) (((x) >> 15) & 0x1) #define EHCI_QTD_IOC 0x00008000 #define EHCI_QTD_GET_BYTES(x) (((x) >> 16) & 0x7fff) #define EHCI_QTD_SET_BYTES(x) ((x) << 16) #define EHCI_QTD_GET_TOGGLE(x) (((x) >> 31) & 0x1) #define EHCI_QTD_SET_TOGGLE(x) ((x) << 31) #define EHCI_QTD_TOGGLE_MASK 0x80000000 #define EHCI_QTD_NBUFFERS 5 volatile uint32_t qtd_buffer[EHCI_QTD_NBUFFERS]; volatile uint32_t qtd_buffer_hi[EHCI_QTD_NBUFFERS]; /* * Extra information needed: */ struct ehci_qtd *obj_next; uint32_t qtd_self; uint16_t len; } __aligned(EHCI_QTD_ALIGN) ehci_qtd_t; /* Queue Head */ typedef struct ehci_qh { volatile uint32_t qh_link; volatile uint32_t qh_endp; #define EHCI_QH_GET_ADDR(x) (((x) >> 0) & 0x7f) /* endpoint addr */ #define EHCI_QH_SET_ADDR(x) (x) #define EHCI_QH_ADDRMASK 0x0000007f #define EHCI_QH_GET_INACT(x) (((x) >> 7) & 0x01) /* inactivate on next */ #define EHCI_QH_INACT 0x00000080 #define EHCI_QH_GET_ENDPT(x) (((x) >> 8) & 0x0f) /* endpoint no */ #define EHCI_QH_SET_ENDPT(x) ((x) << 8) #define EHCI_QH_GET_EPS(x) (((x) >> 12) & 0x03) /* endpoint speed */ #define EHCI_QH_SET_EPS(x) ((x) << 12) #define EHCI_QH_SPEED_FULL 0x0 #define EHCI_QH_SPEED_LOW 0x1 #define EHCI_QH_SPEED_HIGH 0x2 #define EHCI_QH_GET_DTC(x) (((x) >> 14) & 0x01) /* data toggle control */ #define EHCI_QH_DTC 0x00004000 #define EHCI_QH_GET_HRECL(x) (((x) >> 15) & 0x01) /* head of reclamation */ #define EHCI_QH_HRECL 0x00008000 #define EHCI_QH_GET_MPL(x) (((x) >> 16) & 0x7ff) /* max packet len */ #define EHCI_QH_SET_MPL(x) ((x) << 16) #define EHCI_QH_MPLMASK 0x07ff0000 #define EHCI_QH_GET_CTL(x) (((x) >> 27) & 0x01) /* control endpoint */ #define EHCI_QH_CTL 0x08000000 #define EHCI_QH_GET_NRL(x) (((x) >> 28) & 0x0f) /* NAK reload */ #define EHCI_QH_SET_NRL(x) ((x) << 28) volatile uint32_t qh_endphub; #define EHCI_QH_GET_SMASK(x) (((x) >> 0) & 0xff) /* intr sched mask */ #define EHCI_QH_SET_SMASK(x) ((x) << 0) #define EHCI_QH_GET_CMASK(x) (((x) >> 8) & 0xff) /* split completion mask */ #define EHCI_QH_SET_CMASK(x) ((x) << 8) #define EHCI_QH_GET_HUBA(x) (((x) >> 16) & 0x7f) /* hub address */ #define EHCI_QH_SET_HUBA(x) ((x) << 16) #define EHCI_QH_GET_PORT(x) (((x) >> 23) & 0x7f) /* hub port */ #define EHCI_QH_SET_PORT(x) ((x) << 23) #define EHCI_QH_GET_MULT(x) (((x) >> 30) & 0x03) /* pipe multiplier */ #define EHCI_QH_SET_MULT(x) ((x) << 30) volatile uint32_t qh_curqtd; struct { volatile uint32_t qtd_next; volatile uint32_t qtd_altnext; volatile uint32_t qtd_status; volatile uint32_t qtd_buffer[EHCI_QTD_NBUFFERS]; volatile uint32_t qtd_buffer_hi[EHCI_QTD_NBUFFERS]; } __aligned(4) qh_qtd; /* * Extra information needed: */ struct ehci_qh *next; struct ehci_qh *prev; struct ehci_qh *obj_next; uint32_t qh_self; } __aligned(EHCI_QH_ALIGN) ehci_qh_t; /* Periodic Frame Span Traversal Node */ typedef struct { volatile uint32_t fstn_link; volatile uint32_t fstn_back; } __aligned(EHCI_FSTN_ALIGN) ehci_fstn_t; struct ehci_hw_softc { u_int32_t pframes[EHCI_FRAMELIST_COUNT]; /* start TD pointer */ /* structures with highest alignment are first */ ehci_qh_t async_start; ehci_qh_t intr_start[EHCI_VIRTUAL_FRAMELIST_COUNT]; ehci_itd_t isoc_hs_start[EHCI_VIRTUAL_FRAMELIST_COUNT]; ehci_sitd_t isoc_fs_start[EHCI_VIRTUAL_FRAMELIST_COUNT]; }; typedef struct ehci_softc { struct ehci_hw_softc sc_hw; /* hardware structures first */ ehci_qh_t *sc_async_p_last; ehci_qh_t *sc_intr_p_last[EHCI_VIRTUAL_FRAMELIST_COUNT]; u_int16_t sc_intr_stat[EHCI_VIRTUAL_FRAMELIST_COUNT]; ehci_sitd_t *sc_isoc_fs_p_last[EHCI_VIRTUAL_FRAMELIST_COUNT]; ehci_itd_t *sc_isoc_hs_p_last[EHCI_VIRTUAL_FRAMELIST_COUNT]; uint32_t sc_physaddr; struct usbd_bus sc_bus; /* base device */ bus_space_tag_t iot; bus_space_handle_t ioh; bus_size_t sc_size; void *ih; struct resource *io_res; struct resource *irq_res; device_t sc_dev; uint8_t sc_offs; /* offset to operational registers */ uint8_t sc_doorbell_disable; /* set on doorbell failure */ char sc_vendor[16]; /* vendor string for root hub */ int sc_id_vendor; /* vendor ID for root hub */ #if defined(__NetBSD__) void *sc_powerhook; /* cookie from power hook */ void *sc_shutdownhook; /* cookie from shutdown hook */ #endif LIST_HEAD(, usbd_xfer) sc_interrupt_list_head; uint8_t sc_noport; uint8_t sc_addr; /* device address */ uint8_t sc_conf; /* device configuration */ struct usbd_xfer *sc_intrxfer; uint8_t sc_isreset; uint32_t sc_eintrs; uint32_t sc_cmd; /* shadow of cmd register during suspend */ struct __callout sc_tmo_pcd; } ehci_softc_t; #define EREAD1(sc, a) bus_space_read_1((sc)->iot, (sc)->ioh, (a)) #define EREAD2(sc, a) bus_space_read_2((sc)->iot, (sc)->ioh, (a)) #define EREAD4(sc, a) bus_space_read_4((sc)->iot, (sc)->ioh, (a)) #define EWRITE1(sc, a, x) \ bus_space_write_1((sc)->iot, (sc)->ioh, (a), (x)) #define EWRITE2(sc, a, x) \ bus_space_write_2((sc)->iot, (sc)->ioh, (a), (x)) #define EWRITE4(sc, a, x) \ bus_space_write_4((sc)->iot, (sc)->ioh, (a), (x)) #define EOREAD1(sc, a) \ bus_space_read_1((sc)->iot, (sc)->ioh, (sc)->sc_offs+(a)) #define EOREAD2(sc, a) \ bus_space_read_2((sc)->iot, (sc)->ioh, (sc)->sc_offs+(a)) #define EOREAD4(sc, a) \ bus_space_read_4((sc)->iot, (sc)->ioh, (sc)->sc_offs+(a)) #define EOWRITE1(sc, a, x) \ bus_space_write_1((sc)->iot, (sc)->ioh, (sc)->sc_offs+(a), (x)) #define EOWRITE2(sc, a, x) \ bus_space_write_2((sc)->iot, (sc)->ioh, (sc)->sc_offs+(a), (x)) #define EOWRITE4(sc, a, x) \ bus_space_write_4((sc)->iot, (sc)->ioh, (sc)->sc_offs+(a), (x)) usbd_status ehci_init(ehci_softc_t *sc); void ehci_detach(struct ehci_softc *sc); void ehci_suspend(struct ehci_softc *sc); void ehci_resume(struct ehci_softc *sc); void ehci_shutdown(ehci_softc_t *sc); void ehci_interrupt(ehci_softc_t *sc); #endif /* _EHCI_H_ */ pwcbsd/usb/if_aue.c000644 000423 000000 00000117273 10551670753 015007 0ustar00luigiwheel000000 000000 /*- * Copyright (c) 1997, 1998, 1999, 2000 * Bill Paul . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD: src/sys/dev/usb/if_aue.c,v 1.99 2006/09/07 00:06:41 imp Exp $"); /* * ADMtek AN986 Pegasus and AN8511 Pegasus II USB to ethernet driver. * Datasheet is available from http://www.admtek.com.tw. * * Written by Bill Paul * Electrical Engineering Department * Columbia University, New York City */ /* * The Pegasus chip uses four USB "endpoints" to provide 10/100 ethernet * support: the control endpoint for reading/writing registers, burst * read endpoint for packet reception, burst write for packet transmission * and one for "interrupts." The chip uses the same RX filter scheme * as the other ADMtek ethernet parts: one perfect filter entry for the * the station address and a 64-bit multicast hash table. The chip supports * both MII and HomePNA attachments. * * Since the maximum data transfer speed of USB is supposed to be 12Mbps, * you're never really going to get 100Mbps speeds from this device. I * think the idea is to allow the device to connect to 10 or 100Mbps * networks, not necessarily to provide 100Mbps performance. Also, since * the controller uses an external PHY chip, it's possible that board * designers might simply choose a 10Mbps PHY. * * Registers are accessed using usbd_do_request(). Packet transfers are * done using usbd_transfer() and friends. */ /* * NOTE: all function names beginning like "aue_cfg_" can only * be called from within the config thread function ! */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define usbd_config_td_cc aue_config_copy #define usbd_config_td_softc aue_softc #include #include #include #include "usbdevs.h" #include #include #include /* "device miibus" required. See GENERIC if you get errors here. */ #include "miibus_if.h" MODULE_DEPEND(aue, usb, 1, 1, 1); MODULE_DEPEND(aue, ether, 1, 1, 1); MODULE_DEPEND(aue, miibus, 1, 1, 1); #ifdef USB_DEBUG #define DPRINTF(sc,n,fmt,...) \ do { if (aue_debug > (n)) { \ printf("%s:%s: " fmt, (sc)->sc_name, \ __FUNCTION__,## __VA_ARGS__); } } while (0) static int aue_debug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, aue, CTLFLAG_RW, 0, "USB aue"); SYSCTL_INT(_hw_usb_aue, OID_AUTO, debug, CTLFLAG_RW, &aue_debug, 0, "aue debug level"); #else #define DPRINTF(...) #endif /* * Various supported device vendors/products. */ struct aue_type { struct usb_devno aue_dev; u_int16_t aue_flags; }; static const struct aue_type aue_devs[] = { {{ USB_VENDOR_3COM, USB_PRODUCT_3COM_3C460B}, AUE_FLAG_PII }, {{ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX1}, AUE_FLAG_PNA|AUE_FLAG_PII }, {{ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX2}, AUE_FLAG_PII }, {{ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_UFE1000}, AUE_FLAG_LSYS }, {{ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX4}, AUE_FLAG_PNA }, {{ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX5}, AUE_FLAG_PNA }, {{ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX6}, AUE_FLAG_PII }, {{ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX7}, AUE_FLAG_PII }, {{ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX8}, AUE_FLAG_PII }, {{ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX9}, AUE_FLAG_PNA }, {{ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX10}, 0 }, {{ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_DSB650TX_PNA}, 0 }, {{ USB_VENDOR_ACCTON, USB_PRODUCT_ACCTON_USB320_EC}, 0 }, {{ USB_VENDOR_ACCTON, USB_PRODUCT_ACCTON_SS1001}, AUE_FLAG_PII }, {{ USB_VENDOR_ADMTEK, USB_PRODUCT_ADMTEK_PEGASUS}, AUE_FLAG_PNA }, {{ USB_VENDOR_ADMTEK, USB_PRODUCT_ADMTEK_PEGASUSII}, AUE_FLAG_PII }, {{ USB_VENDOR_ADMTEK, USB_PRODUCT_ADMTEK_PEGASUSII_2}, AUE_FLAG_PII }, {{ USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_USB2LAN}, AUE_FLAG_PII }, {{ USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USB100}, 0 }, {{ USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USBLP100}, AUE_FLAG_PNA }, {{ USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USBEL100}, 0 }, {{ USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USBE100}, AUE_FLAG_PII }, {{ USB_VENDOR_COREGA, USB_PRODUCT_COREGA_FETHER_USB_TX}, 0 }, {{ USB_VENDOR_COREGA, USB_PRODUCT_COREGA_FETHER_USB_TXS},AUE_FLAG_PII }, {{ USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX4}, AUE_FLAG_LSYS|AUE_FLAG_PII }, {{ USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX1}, AUE_FLAG_LSYS }, {{ USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX}, AUE_FLAG_LSYS }, {{ USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX_PNA}, AUE_FLAG_PNA }, {{ USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX3}, AUE_FLAG_LSYS|AUE_FLAG_PII }, {{ USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX2}, AUE_FLAG_LSYS|AUE_FLAG_PII }, {{ USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650}, AUE_FLAG_LSYS }, {{ USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBTX0}, 0 }, {{ USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBTX1}, AUE_FLAG_LSYS }, {{ USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBTX2}, 0 }, {{ USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBTX3}, AUE_FLAG_LSYS }, {{ USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBLTX}, AUE_FLAG_PII }, {{ USB_VENDOR_ELSA, USB_PRODUCT_ELSA_USB2ETHERNET}, 0 }, {{ USB_VENDOR_HAWKING, USB_PRODUCT_HAWKING_UF100}, AUE_FLAG_PII }, {{ USB_VENDOR_HP, USB_PRODUCT_HP_HN210E}, AUE_FLAG_PII }, {{ USB_VENDOR_IODATA, USB_PRODUCT_IODATA_USBETTX}, 0 }, {{ USB_VENDOR_IODATA, USB_PRODUCT_IODATA_USBETTXS}, AUE_FLAG_PII }, {{ USB_VENDOR_KINGSTON, USB_PRODUCT_KINGSTON_KNU101TX}, 0 }, {{ USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10TX1}, AUE_FLAG_LSYS|AUE_FLAG_PII }, {{ USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10T}, AUE_FLAG_LSYS }, {{ USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB100TX}, AUE_FLAG_LSYS }, {{ USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB100H1}, AUE_FLAG_LSYS|AUE_FLAG_PNA }, {{ USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10TA}, AUE_FLAG_LSYS }, {{ USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10TX2}, AUE_FLAG_LSYS|AUE_FLAG_PII }, {{ USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_MN110}, AUE_FLAG_PII }, {{ USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUATX1}, 0 }, {{ USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUATX5}, 0 }, {{ USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUA2TX5}, AUE_FLAG_PII }, {{ USB_VENDOR_SIEMENS, USB_PRODUCT_SIEMENS_SPEEDSTREAM}, AUE_FLAG_PII }, {{ USB_VENDOR_SMARTBRIDGES, USB_PRODUCT_SMARTBRIDGES_SMARTNIC},AUE_FLAG_PII }, {{ USB_VENDOR_SMC, USB_PRODUCT_SMC_2202USB}, 0 }, {{ USB_VENDOR_SMC, USB_PRODUCT_SMC_2206USB}, AUE_FLAG_PII }, {{ USB_VENDOR_SOHOWARE, USB_PRODUCT_SOHOWARE_NUB100}, 0 }, }; #define aue_lookup(v, p) ((const struct aue_type *)usb_lookup(aue_devs, v, p)) static device_probe_t aue_probe; static device_attach_t aue_attach; static device_detach_t aue_detach; static device_shutdown_t aue_shutdown; static void aue_cfg_do_request(struct aue_softc *sc, usb_device_request_t *req, void *data); static u_int8_t aue_cfg_csr_read_1(struct aue_softc *sc, u_int16_t reg); static u_int16_t aue_cfg_csr_read_2(struct aue_softc *sc, u_int16_t reg); static void aue_cfg_csr_write_1(struct aue_softc *sc, u_int16_t reg, u_int8_t val); static void aue_cfg_csr_write_2(struct aue_softc *sc, u_int16_t reg, u_int16_t val); static void aue_cfg_eeprom_getword(struct aue_softc *sc, u_int8_t addr, u_int8_t *dest); static void aue_cfg_read_eeprom(struct aue_softc *sc, u_int8_t *dest, u_int16_t off, u_int16_t len); static int aue_cfg_miibus_readreg(device_t dev, int phy, int reg); static int aue_cfg_miibus_writereg(device_t dev, int phy, int reg, int data); static void aue_cfg_miibus_statchg(device_t dev); static void aue_cfg_setmulti(struct aue_softc *sc, struct aue_config_copy *cc, u_int16_t refcount); static void aue_cfg_reset_pegasus_II(struct aue_softc *sc); static void aue_cfg_reset(struct aue_softc *sc); static void aue_cfg_first_time_setup(struct aue_softc *sc, struct aue_config_copy *cc, u_int16_t refcount); static void aue_intr_clear_stall_callback(struct usbd_xfer *xfer); static void aue_intr_callback(struct usbd_xfer *xfer); static void aue_bulk_read_clear_stall_callback(struct usbd_xfer *xfer); static void aue_bulk_read_callback(struct usbd_xfer *xfer); static void aue_bulk_write_clear_stall_callback(struct usbd_xfer *xfer); static void aue_bulk_write_callback(struct usbd_xfer *xfer); static void aue_config_copy(struct aue_softc *sc, struct aue_config_copy *cc, u_int16_t refcount); static void aue_cfg_tick(struct aue_softc *sc, struct aue_config_copy *cc, u_int16_t refcount); static void aue_start_cb(struct ifnet *ifp); static void aue_init_cb(void *arg); static void aue_start_transfers(struct aue_softc *sc); static void aue_cfg_init(struct aue_softc *sc, struct aue_config_copy *cc, u_int16_t refcount); static void aue_cfg_promisc_upd(struct aue_softc *sc, struct aue_config_copy *cc, u_int16_t refcount); static int aue_ifmedia_upd_cb(struct ifnet *ifp); static void aue_cfg_ifmedia_upd(struct aue_softc *sc, struct aue_config_copy *cc, u_int16_t refcount); static void aue_ifmedia_sts_cb(struct ifnet *ifp, struct ifmediareq *ifmr); static int aue_ioctl_cb(struct ifnet *ifp, u_long command, caddr_t data); static void aue_watchdog(void *arg); static void aue_cfg_stop(struct aue_softc *sc, struct aue_config_copy *cc, u_int16_t refcount); static const struct usbd_config aue_config[AUE_ENDPT_MAX] = { [0] = { .type = UE_BULK, .endpoint = -1, /* any */ .direction = UE_DIR_OUT, .bufsize = (MCLBYTES + 2), .flags = (USBD_USE_DMA|USBD_FORCE_SHORT_XFER), .callback = &aue_bulk_write_callback, .timeout = 10000, /* 10 seconds */ }, [1] = { .type = UE_BULK, .endpoint = -1, /* any */ .direction = UE_DIR_IN, .bufsize = (MCLBYTES + 4 + ETHER_CRC_LEN), .flags = (USBD_USE_DMA|USBD_SHORT_XFER_OK), .callback = &aue_bulk_read_callback, }, [2] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .flags = USBD_USE_DMA, .callback = &aue_bulk_write_clear_stall_callback, .timeout = 1000, /* 1 second */ }, [3] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .flags = USBD_USE_DMA, .callback = &aue_bulk_read_clear_stall_callback, .timeout = 1000, /* 1 second */ }, [4] = { .type = UE_INTERRUPT, .endpoint = -1, /* any */ .direction = UE_DIR_IN, .flags = USBD_SHORT_XFER_OK, .bufsize = 0, /* use wMaxPacketSize */ .callback = &aue_intr_callback, }, [5] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .flags = USBD_USE_DMA, .callback = &aue_intr_clear_stall_callback, .timeout = 1000, /* 1 second */ }, }; static device_method_t aue_methods[] = { /* Device interface */ DEVMETHOD(device_probe, aue_probe), DEVMETHOD(device_attach, aue_attach), DEVMETHOD(device_detach, aue_detach), DEVMETHOD(device_shutdown, aue_shutdown), /* bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_driver_added, bus_generic_driver_added), /* MII interface */ DEVMETHOD(miibus_readreg, aue_cfg_miibus_readreg), DEVMETHOD(miibus_writereg, aue_cfg_miibus_writereg), DEVMETHOD(miibus_statchg, aue_cfg_miibus_statchg), { 0, 0 } }; static driver_t aue_driver = { .name = "aue", .methods = aue_methods, .size = sizeof(struct aue_softc) }; static devclass_t aue_devclass; DRIVER_MODULE(aue, uhub, aue_driver, aue_devclass, usbd_driver_load, 0); DRIVER_MODULE(miibus, aue, miibus_driver, miibus_devclass, 0, 0); static void aue_cfg_do_request(struct aue_softc *sc, usb_device_request_t *req, void *data) { u_int16_t length; usbd_status err; if (usbd_config_td_is_gone(&(sc->sc_config_td))) { goto error; } err = usbd_do_request_flags_mtx(sc->sc_udev, &(sc->sc_mtx), req, data, 0, NULL, 1000); if (err) { DPRINTF(sc, 0, "device request failed, err=%s " "(ignored)\n", usbd_errstr(err)); error: length = UGETW(req->wLength); if ((req->bmRequestType & UT_READ) && length) { bzero(data, length); } } return; } #define AUE_CFG_SETBIT(sc, reg, x) \ aue_cfg_csr_write_1(sc, reg, aue_cfg_csr_read_1(sc, reg) | (x)) #define AUE_CFG_CLRBIT(sc, reg, x) \ aue_cfg_csr_write_1(sc, reg, aue_cfg_csr_read_1(sc, reg) & ~(x)) static u_int8_t aue_cfg_csr_read_1(struct aue_softc *sc, u_int16_t reg) { usb_device_request_t req; u_int8_t val; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = AUE_UR_READREG; USETW(req.wValue, 0); USETW(req.wIndex, reg); USETW(req.wLength, 1); aue_cfg_do_request(sc, &req, &val); return val; } static u_int16_t aue_cfg_csr_read_2(struct aue_softc *sc, u_int16_t reg) { usb_device_request_t req; u_int16_t val; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = AUE_UR_READREG; USETW(req.wValue, 0); USETW(req.wIndex, reg); USETW(req.wLength, 2); aue_cfg_do_request(sc, &req, &val); return le16toh(val); } static void aue_cfg_csr_write_1(struct aue_softc *sc, u_int16_t reg, u_int8_t val) { usb_device_request_t req; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = AUE_UR_WRITEREG; req.wValue[0] = val; req.wValue[1] = 0; USETW(req.wIndex, reg); USETW(req.wLength, 1); aue_cfg_do_request(sc, &req, &val); return; } static void aue_cfg_csr_write_2(struct aue_softc *sc, u_int16_t reg, u_int16_t val) { usb_device_request_t req; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = AUE_UR_WRITEREG; USETW(req.wValue, val); USETW(req.wIndex, reg); USETW(req.wLength, 2); val = htole16(val); aue_cfg_do_request(sc, &req, &val); return; } /* * Read a word of data stored in the EEPROM at address 'addr.' */ static void aue_cfg_eeprom_getword(struct aue_softc *sc, u_int8_t addr, u_int8_t *dest) { u_int16_t i; aue_cfg_csr_write_1(sc, AUE_EE_REG, addr); aue_cfg_csr_write_1(sc, AUE_EE_CTL, AUE_EECTL_READ); for (i = 0; ; i++) { if (i < AUE_TIMEOUT) { if (aue_cfg_csr_read_1(sc, AUE_EE_CTL) & AUE_EECTL_DONE) { break; } if (usbd_config_td_sleep(&(sc->sc_config_td), hz/100)) { break; } } else { DPRINTF(sc, 0, "EEPROM read timed out!\n"); break; } } i = aue_cfg_csr_read_2(sc, AUE_EE_DATA); dest[0] = (i & 0xFF); dest[1] = (i >> 8); return; } /* * Read a sequence of words from the EEPROM. */ static void aue_cfg_read_eeprom(struct aue_softc *sc, u_int8_t *dest, u_int16_t off, u_int16_t len) { u_int16_t i; for (i = 0; i < len; i++) { aue_cfg_eeprom_getword(sc, off + i, dest + (i * 2)); } return; } static int aue_cfg_miibus_readreg(device_t dev, int phy, int reg) { struct aue_softc * sc = device_get_softc(dev); u_int16_t i; mtx_lock(&(sc->sc_mtx)); /* XXX */ /* * The Am79C901 HomePNA PHY actually contains * two transceivers: a 1Mbps HomePNA PHY and a * 10Mbps full/half duplex ethernet PHY with * NWAY autoneg. However in the ADMtek adapter, * only the 1Mbps PHY is actually connected to * anything, so we ignore the 10Mbps one. It * happens to be configured for MII address 3, * so we filter that out. */ if ((sc->sc_vendor == USB_VENDOR_ADMTEK) && (sc->sc_product == USB_PRODUCT_ADMTEK_PEGASUS)) { if (phy == 3) { i = 0; goto done; } #ifdef notdef if (phy != 1) { i = 0; goto done; } #endif } aue_cfg_csr_write_1(sc, AUE_PHY_ADDR, phy); aue_cfg_csr_write_1(sc, AUE_PHY_CTL, reg | AUE_PHYCTL_READ); for (i = 0; ; i++) { if (i < AUE_TIMEOUT) { if (aue_cfg_csr_read_1(sc, AUE_PHY_CTL) & AUE_PHYCTL_DONE) { break; } if (usbd_config_td_sleep(&(sc->sc_config_td), hz/100)) { break; } } else { DPRINTF(sc, 0, "MII read timed out\n"); break; } } i = aue_cfg_csr_read_2(sc, AUE_PHY_DATA); done: mtx_unlock(&(sc->sc_mtx)); /* XXX */ return i; } static int aue_cfg_miibus_writereg(device_t dev, int phy, int reg, int data) { struct aue_softc * sc = device_get_softc(dev); u_int16_t i; if (phy == 3) { return (0); } mtx_lock(&(sc->sc_mtx)); /* XXX */ aue_cfg_csr_write_2(sc, AUE_PHY_DATA, data); aue_cfg_csr_write_1(sc, AUE_PHY_ADDR, phy); aue_cfg_csr_write_1(sc, AUE_PHY_CTL, reg | AUE_PHYCTL_WRITE); for (i = 0; ; i++) { if (i < AUE_TIMEOUT) { if (aue_cfg_csr_read_1(sc, AUE_PHY_CTL) & AUE_PHYCTL_DONE) { break; } if (usbd_config_td_sleep(&(sc->sc_config_td), hz/100)) { break; } } else { DPRINTF(sc, 0, "MII write timed out\n"); break; } } mtx_unlock(&(sc->sc_mtx)); /* XXX */ return(0); } static void aue_cfg_miibus_statchg(device_t dev) { struct aue_softc * sc = device_get_softc(dev); struct mii_data * mii = GET_MII(sc); mtx_lock(&(sc->sc_mtx)); /* XXX */ AUE_CFG_CLRBIT(sc, AUE_CTL0, AUE_CTL0_RX_ENB | AUE_CTL0_TX_ENB); if (IFM_SUBTYPE(mii->mii_media_active) == IFM_100_TX) { AUE_CFG_SETBIT(sc, AUE_CTL1, AUE_CTL1_SPEEDSEL); } else { AUE_CFG_CLRBIT(sc, AUE_CTL1, AUE_CTL1_SPEEDSEL); } if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) { AUE_CFG_SETBIT(sc, AUE_CTL1, AUE_CTL1_DUPLEX); } else { AUE_CFG_CLRBIT(sc, AUE_CTL1, AUE_CTL1_DUPLEX); } AUE_CFG_SETBIT(sc, AUE_CTL0, AUE_CTL0_RX_ENB | AUE_CTL0_TX_ENB); /* * Set the LED modes on the LinkSys adapter. * This turns on the 'dual link LED' bin in the auxmode * register of the Broadcom PHY. */ if (sc->sc_flags & AUE_FLAG_LSYS) { u_int16_t auxmode; auxmode = aue_cfg_miibus_readreg(dev, 0, 0x1b); aue_cfg_miibus_writereg(dev, 0, 0x1b, auxmode | 0x04); } mtx_unlock(&(sc->sc_mtx)); /* XXX */ return; } static void aue_cfg_setmulti(struct aue_softc *sc, struct aue_config_copy *cc, u_int16_t refcount) { u_int16_t i; if (cc == NULL) { /* nothing to do */ return; } if ((cc->if_flags & IFF_ALLMULTI) || (cc->if_flags & IFF_PROMISC)) { AUE_CFG_SETBIT(sc, AUE_CTL0, AUE_CTL0_ALLMULTI); return; } AUE_CFG_CLRBIT(sc, AUE_CTL0, AUE_CTL0_ALLMULTI); /* clear existing ones */ for (i = 0; i < 8; i++) { aue_cfg_csr_write_1(sc, AUE_MAR0 + i, 0); } /* now program new ones */ for (i = 0; i < 8; i++) { aue_cfg_csr_write_1(sc, AUE_MAR0 + i, cc->if_hash[i]); } return; } static void aue_cfg_reset_pegasus_II(struct aue_softc *sc) { /* Magic constants taken from Linux driver. */ aue_cfg_csr_write_1(sc, AUE_REG_1D, 0); aue_cfg_csr_write_1(sc, AUE_REG_7B, 2); #if 0 if ((sc->sc_flags & HAS_HOME_PNA) && mii_mode) aue_cfg_csr_write_1(sc, AUE_REG_81, 6); else #endif aue_cfg_csr_write_1(sc, AUE_REG_81, 2); return; } static void aue_cfg_reset(struct aue_softc *sc) { u_int16_t i; AUE_CFG_SETBIT(sc, AUE_CTL1, AUE_CTL1_RESETMAC); for (i = 0; ; i++) { if (i < AUE_TIMEOUT) { if (!(aue_cfg_csr_read_1(sc, AUE_CTL1) & AUE_CTL1_RESETMAC)) { break; } if (usbd_config_td_sleep(&(sc->sc_config_td), hz/100)) { break; } } else { DPRINTF(sc, 0, "reset timed out\n"); break; } } /* * The PHY(s) attached to the Pegasus chip may be held * in reset until we flip on the GPIO outputs. Make sure * to set the GPIO pins high so that the PHY(s) will * be enabled. * * Note: We force all of the GPIO pins low first, *then* * enable the ones we want. */ aue_cfg_csr_write_1(sc, AUE_GPIO0, (AUE_GPIO_OUT0|AUE_GPIO_SEL0)); aue_cfg_csr_write_1(sc, AUE_GPIO0, (AUE_GPIO_OUT0|AUE_GPIO_SEL0| AUE_GPIO_SEL1)); if (sc->sc_flags & AUE_FLAG_LSYS) { /* Grrr. LinkSys has to be different from everyone else. */ aue_cfg_csr_write_1(sc, AUE_GPIO0, (AUE_GPIO_SEL0 | AUE_GPIO_SEL1)); aue_cfg_csr_write_1(sc, AUE_GPIO0, (AUE_GPIO_SEL0 | AUE_GPIO_SEL1 | AUE_GPIO_OUT0)); } if (sc->sc_flags & AUE_FLAG_PII) { aue_cfg_reset_pegasus_II(sc); } /* wait a little while for the chip to get its brains in order: */ usbd_config_td_sleep(&(sc->sc_config_td), hz/100); return; } /* * Probe for a Pegasus chip. */ static int aue_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); if (uaa->iface != NULL) { return UMATCH_NONE; } return (aue_lookup(uaa->vendor, uaa->product) != NULL ? UMATCH_VENDOR_PRODUCT : UMATCH_NONE); } /* * Attach the interface. Allocate softc structures, do ifmedia * setup and ethernet/BPF attach. */ static int aue_attach(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct aue_softc *sc = device_get_softc(dev); int32_t error; if (sc == NULL) { return ENOMEM; } sc->sc_udev = uaa->device; sc->sc_dev = dev; sc->sc_unit = device_get_unit(dev); sc->sc_flags = aue_lookup(uaa->vendor, uaa->product)->aue_flags; sc->sc_product = uaa->product; sc->sc_vendor = uaa->vendor; usbd_set_desc(dev, uaa->device); snprintf(sc->sc_name, sizeof(sc->sc_name), "%s", device_get_nameunit(dev)); mtx_init(&(sc->sc_mtx), "aue lock", NULL, MTX_DEF | MTX_RECURSE); __callout_init_mtx(&(sc->sc_watchdog), &(sc->sc_mtx), CALLOUT_RETURNUNLOCKED); error = usbd_set_config_no(uaa->device, AUE_CONFIG_NO, 0); if (error) { device_printf(dev, "setting config " "number failed!\n"); goto detach; } error = usbd_transfer_setup(uaa->device, AUE_IFACE_IDX, sc->sc_xfer, aue_config, AUE_ENDPT_MAX, sc, &(sc->sc_mtx)); if (error) { device_printf(dev, "allocating USB " "transfers failed!\n"); goto detach; } error = usbd_config_td_setup(&(sc->sc_config_td), sc, &(sc->sc_mtx), &aue_config_copy, NULL, sizeof(struct aue_config_copy), 16); if (error) { device_printf(dev, "could not setup config " "thread!\n"); goto detach; } mtx_lock(&(sc->sc_mtx)); sc->sc_flags |= AUE_FLAG_WAIT_LINK; /* start setup */ usbd_config_td_queue_command (&(sc->sc_config_td), &aue_cfg_first_time_setup, 0); /* start watchdog (will exit mutex) */ aue_watchdog(sc); return 0; /* success */ detach: aue_detach(dev); return ENXIO; /* failure */ } static void aue_cfg_first_time_setup(struct aue_softc *sc, struct aue_config_copy *cc, u_int16_t refcount) { struct ifnet * ifp; int error; u_int8_t eaddr[min(ETHER_ADDR_LEN,6)]; if (cc == NULL) { return; } /* reset the adapter */ aue_cfg_reset(sc); /* set default value */ bzero(eaddr, sizeof(eaddr)); /* get station address from the EEPROM */ aue_cfg_read_eeprom(sc, eaddr, 0, 3); mtx_unlock(&(sc->sc_mtx)); ifp = if_alloc(IFT_ETHER); mtx_lock(&(sc->sc_mtx)); if (ifp == NULL) { printf("%s: could not if_alloc()\n", sc->sc_name); goto done; } ifp->if_softc = sc; if_initname(ifp, "aue", sc->sc_unit); ifp->if_mtu = ETHERMTU; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = aue_ioctl_cb; ifp->if_start = aue_start_cb; ifp->if_watchdog = NULL; ifp->if_init = aue_init_cb; ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; /* XXX need Giant when accessing * the device structures ! */ mtx_unlock(&(sc->sc_mtx)); mtx_lock(&Giant); error = mii_phy_probe(sc->sc_dev, &(sc->sc_miibus), &aue_ifmedia_upd_cb, &aue_ifmedia_sts_cb); mtx_unlock(&Giant); mtx_lock(&(sc->sc_mtx)); /* * Do MII setup. * NOTE: Doing this causes child devices to be attached to us, * which we would normally disconnect at in the detach routine * using device_delete_child(). However the USB code is set up * such that when this driver is removed, all children devices * are removed as well. In effect, the USB code ends up detaching * all of our children for us, so we don't have to do is ourselves * in aue_detach(). It's important to point this out since if * we *do* try to detach the child devices ourselves, we will * end up getting the children deleted twice, which will crash * the system. */ if (error) { printf("%s: MII without any PHY!\n", sc->sc_name); if_free(ifp); goto done; } sc->sc_ifp = ifp; /* * Call MI attach routine. */ ether_ifattach(ifp, eaddr); done: return; } static int aue_detach(device_t dev) { struct aue_softc * sc = device_get_softc(dev); struct ifnet * ifp; mtx_lock(&(sc->sc_mtx)); usbd_config_td_stop(&(sc->sc_config_td)); __callout_stop(&(sc->sc_watchdog)); aue_cfg_stop(sc, NULL, 0); ifp = sc->sc_ifp; mtx_unlock(&(sc->sc_mtx)); /* get rid of any late children */ bus_generic_detach(dev); if (ifp) { ether_ifdetach(ifp); if_free(ifp); } usbd_transfer_unsetup(sc->sc_xfer, AUE_ENDPT_MAX); usbd_config_td_unsetup(&(sc->sc_config_td)); __callout_drain(&(sc->sc_watchdog)); mtx_destroy(&(sc->sc_mtx)); return 0; } static void aue_intr_clear_stall_callback(struct usbd_xfer *xfer) { struct aue_softc *sc = xfer->priv_sc; struct usbd_xfer *xfer_other = sc->sc_xfer[4]; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, xfer_other); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, xfer_other); sc->sc_flags &= ~AUE_FLAG_INTR_STALL; usbd_transfer_start(xfer_other); return; tr_error: /* bomb out */ sc->sc_flags &= ~AUE_FLAG_INTR_STALL; DPRINTF(sc, 0, "interrupt read pipe stopped\n"); return; } static void aue_intr_callback(struct usbd_xfer *xfer) { struct aue_softc *sc = xfer->priv_sc; struct ifnet *ifp = sc->sc_ifp; struct aue_intrpkt *p = xfer->buffer; USBD_CHECK_STATUS(xfer); tr_transferred: if (ifp && (ifp->if_drv_flags & IFF_DRV_RUNNING) && (xfer->actlen >= sizeof(*p))) { if (p->aue_txstat0) { ifp->if_oerrors++; } if (p->aue_txstat0 & (AUE_TXSTAT0_LATECOLL & AUE_TXSTAT0_EXCESSCOLL)) { ifp->if_collisions++; } } tr_setup: if (sc->sc_flags & AUE_FLAG_INTR_STALL) { usbd_transfer_start(sc->sc_xfer[5]); } else { usbd_start_hardware(xfer); } return; tr_error: if (xfer->error != USBD_CANCELLED) { /* start clear stall */ sc->sc_flags |= AUE_FLAG_INTR_STALL; usbd_transfer_start(sc->sc_xfer[5]); } return; } static void aue_bulk_read_clear_stall_callback(struct usbd_xfer *xfer) { struct aue_softc *sc = xfer->priv_sc; struct usbd_xfer *xfer_other = sc->sc_xfer[1]; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, xfer_other); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, xfer_other); sc->sc_flags &= ~AUE_FLAG_READ_STALL; usbd_transfer_start(xfer_other); return; tr_error: /* bomb out */ sc->sc_flags &= ~AUE_FLAG_READ_STALL; DPRINTF(sc, 0, "bulk read pipe stopped\n"); return; } static void aue_bulk_read_callback(struct usbd_xfer *xfer) { struct aue_softc *sc = xfer->priv_sc; struct ifnet *ifp = sc->sc_ifp; struct mbuf *m; USBD_CHECK_STATUS(xfer); tr_error: if (xfer->error != USBD_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= AUE_FLAG_READ_STALL; usbd_transfer_start(sc->sc_xfer[3]); } DPRINTF(sc, 0, "bulk read error, %s\n", usbd_errstr(xfer->error)); return; tr_transferred: if (xfer->actlen <= (4 + ETHER_CRC_LEN)) { ifp->if_ierrors++; goto tr_setup; } usbd_copy_out(&(xfer->buf_data), xfer->actlen - 4, &(sc->sc_rxpkt), sizeof(sc->sc_rxpkt)); /* turn off all the non-error bits * in the rx status word: */ sc->sc_rxpkt.aue_rxstat &= AUE_RXSTAT_MASK; if (sc->sc_rxpkt.aue_rxstat) { ifp->if_ierrors++; goto tr_setup; } /* No errors; receive the packet. */ xfer->actlen -= (4 + ETHER_CRC_LEN); m = usbd_ether_get_mbuf(); if (m == NULL) { ifp->if_ierrors++; goto tr_setup; } xfer->actlen = min(xfer->actlen, m->m_len); usbd_copy_out(&(xfer->buf_data), 0, m->m_data, xfer->actlen); ifp->if_ipackets++; m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = m->m_len = xfer->actlen; (ifp->if_input)(ifp, m); tr_setup: if (sc->sc_flags & AUE_FLAG_READ_STALL) { usbd_transfer_start(sc->sc_xfer[3]); } else { usbd_start_hardware(xfer); } return; } static void aue_bulk_write_clear_stall_callback(struct usbd_xfer *xfer) { struct aue_softc *sc = xfer->priv_sc; struct usbd_xfer *xfer_other = sc->sc_xfer[0]; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, xfer_other); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, xfer_other); sc->sc_flags &= ~AUE_FLAG_WRITE_STALL; usbd_transfer_start(xfer_other); return; tr_error: /* bomb out */ sc->sc_flags &= ~AUE_FLAG_WRITE_STALL; DPRINTF(sc, 0, "bulk write pipe stopped\n"); return; } static void aue_bulk_write_callback(struct usbd_xfer *xfer) { struct aue_softc *sc = xfer->priv_sc; struct ifnet *ifp = sc->sc_ifp; struct mbuf *m; u_int8_t buf[2]; USBD_CHECK_STATUS(xfer); tr_error: DPRINTF(sc, 10, "transfer error, %s\n", usbd_errstr(xfer->error)); if (xfer->error != USBD_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= AUE_FLAG_WRITE_STALL; usbd_transfer_start(sc->sc_xfer[2]); } ifp->if_oerrors++; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; return; tr_transferred: DPRINTF(sc, 10, "transfer complete\n"); ifp->if_opackets++; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; tr_setup: if (sc->sc_flags & AUE_FLAG_WRITE_STALL) { usbd_transfer_start(sc->sc_xfer[2]); goto done; } if (sc->sc_flags & AUE_FLAG_WAIT_LINK) { /* don't send anything * if there is no link ! */ goto done; } IF_DEQUEUE(&(ifp->if_snd), m); if (m == NULL) { goto done; } if (m->m_pkthdr.len > MCLBYTES) { m->m_pkthdr.len = MCLBYTES; } xfer->length = (m->m_pkthdr.len + 2); /* * The ADMtek documentation says that the packet length is * supposed to be specified in the first two bytes of the * transfer, however it actually seems to ignore this info * and base the frame size on the bulk transfer length. */ buf[0] = (u_int8_t)(m->m_pkthdr.len); buf[1] = (u_int8_t)(m->m_pkthdr.len >> 8); usbd_copy_in(&(xfer->buf_data), 0, buf, 2); usbd_m_copy_in(&(xfer->buf_data), 2, m, 0, m->m_pkthdr.len); /* * if there's a BPF listener, bounce a copy * of this frame to him: */ BPF_MTAP(ifp, m); m_freem(m); usbd_start_hardware(xfer); ifp->if_drv_flags |= IFF_DRV_OACTIVE; done: return; } #define AUE_BITS 6 static void aue_config_copy(struct aue_softc *sc, struct aue_config_copy *cc, u_int16_t refcount) { struct ifnet * ifp = sc->sc_ifp; struct ifmultiaddr * ifma; u_int8_t h; u_int8_t i; bzero(cc, sizeof(*cc)); if (ifp) { for (i = 0; i < ETHER_ADDR_LEN; i++) { cc->if_lladdr[i] = IF_LLADDR(ifp)[i]; } cc->if_flags = ifp->if_flags; /* compute hash bits for multicast filter */ IF_ADDR_LOCK(ifp); TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) { continue; } h = (ether_crc32_le (LLADDR((struct sockaddr_dl *)(ifma->ifma_addr)), ETHER_ADDR_LEN)) & ((1 << AUE_BITS) - 1); cc->if_hash[(h >> 3)] |= (1 << (h & 7)); } IF_ADDR_UNLOCK(ifp); } return; } static void aue_cfg_tick(struct aue_softc *sc, struct aue_config_copy *cc, u_int16_t refcount) { struct ifnet * ifp = sc->sc_ifp; struct mii_data * mii = GET_MII(sc); if ((cc == NULL) || (ifp == NULL) || (mii == NULL)) { /* not ready */ return; } mii_tick(mii); mii_pollstat(mii); if ((sc->sc_flags & AUE_FLAG_WAIT_LINK) && (mii->mii_media_status & IFM_ACTIVE) && (IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE)) { sc->sc_flags &= ~AUE_FLAG_WAIT_LINK; } sc->sc_media_active = mii->mii_media_active; sc->sc_media_status = mii->mii_media_status; /* start stopped transfers, if any */ aue_start_transfers(sc); return; } static void aue_start_cb(struct ifnet *ifp) { struct aue_softc *sc = ifp->if_softc; mtx_lock(&(sc->sc_mtx)); aue_start_transfers(sc); mtx_unlock(&(sc->sc_mtx)); return; } static void aue_init_cb(void *arg) { struct aue_softc *sc = arg; mtx_lock(&(sc->sc_mtx)); usbd_config_td_queue_command (&(sc->sc_config_td), &aue_cfg_init, 0); mtx_unlock(&(sc->sc_mtx)); return; } static void aue_start_transfers(struct aue_softc *sc) { if ((sc->sc_flags & AUE_FLAG_LL_READY) && (sc->sc_flags & AUE_FLAG_HL_READY)) { /* start the USB transfers, * if not already started: */ usbd_transfer_start(sc->sc_xfer[4]); usbd_transfer_start(sc->sc_xfer[1]); usbd_transfer_start(sc->sc_xfer[0]); } return; } static void aue_cfg_init(struct aue_softc *sc, struct aue_config_copy *cc, u_int16_t refcount) { struct mii_data *mii = GET_MII(sc); u_int8_t i; if (cc == NULL) { /* immediate configuration */ struct ifnet *ifp = sc->sc_ifp; aue_cfg_stop(sc, NULL, 0); ifp->if_drv_flags |= IFF_DRV_RUNNING; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; sc->sc_flags |= AUE_FLAG_HL_READY; return; } /* * Cancel pending I/O */ aue_cfg_stop(sc, cc, 0); /* Set MAC address */ for (i = 0; i < ETHER_ADDR_LEN; i++) { aue_cfg_csr_write_1(sc, AUE_PAR0 + i, cc->if_lladdr[i]); } /* update promiscuous setting */ aue_cfg_promisc_upd(sc, cc, 0); /* load the multicast filter */ aue_cfg_setmulti(sc, cc, 0); /* enable RX and TX */ aue_cfg_csr_write_1(sc, AUE_CTL0, (AUE_CTL0_RXSTAT_APPEND | AUE_CTL0_RX_ENB)); AUE_CFG_SETBIT(sc, AUE_CTL0, AUE_CTL0_TX_ENB); AUE_CFG_SETBIT(sc, AUE_CTL2, AUE_CTL2_EP3_CLR); mii_mediachg(mii); sc->sc_flags |= (AUE_FLAG_READ_STALL| AUE_FLAG_WRITE_STALL| AUE_FLAG_LL_READY); aue_start_transfers(sc); return; } static void aue_cfg_promisc_upd(struct aue_softc *sc, struct aue_config_copy *cc, u_int16_t refcount) { if (cc == NULL) { /* nothing to do */ return; } /* if we want promiscuous mode, set the allframes bit: */ if (cc->if_flags & IFF_PROMISC) { AUE_CFG_SETBIT(sc, AUE_CTL2, AUE_CTL2_RX_PROMISC); } else { AUE_CFG_CLRBIT(sc, AUE_CTL2, AUE_CTL2_RX_PROMISC); } return; } /* * Set media options. */ static int aue_ifmedia_upd_cb(struct ifnet *ifp) { struct aue_softc *sc = ifp->if_softc; mtx_lock(&(sc->sc_mtx)); usbd_config_td_queue_command (&(sc->sc_config_td), &aue_cfg_ifmedia_upd, 0); mtx_unlock(&(sc->sc_mtx)); return 0; } static void aue_cfg_ifmedia_upd(struct aue_softc *sc, struct aue_config_copy *cc, u_int16_t refcount) { struct ifnet * ifp = sc->sc_ifp; struct mii_data * mii = GET_MII(sc); if ((cc == NULL) || (ifp == NULL) || (mii == NULL)) { /* not ready */ return; } sc->sc_flags |= AUE_FLAG_WAIT_LINK; if (mii->mii_instance) { struct mii_softc *miisc; LIST_FOREACH(miisc, &mii->mii_phys, mii_list) { mii_phy_reset(miisc); } } mii_mediachg(mii); return; } /* * Report current media status. */ static void aue_ifmedia_sts_cb(struct ifnet *ifp, struct ifmediareq *ifmr) { struct aue_softc *sc = ifp->if_softc; mtx_lock(&(sc->sc_mtx)); ifmr->ifm_active = sc->sc_media_active; ifmr->ifm_status = sc->sc_media_status; mtx_unlock(&(sc->sc_mtx)); return; } static int aue_ioctl_cb(struct ifnet *ifp, u_long command, caddr_t data) { struct aue_softc * sc = ifp->if_softc; struct mii_data * mii; int error = 0; mtx_lock(&(sc->sc_mtx)); switch(command) { case SIOCSIFFLAGS: if (ifp->if_flags & IFF_UP) { if (ifp->if_drv_flags & IFF_DRV_RUNNING) { usbd_config_td_queue_command (&(sc->sc_config_td), &aue_cfg_promisc_upd, 0); } else { usbd_config_td_queue_command (&(sc->sc_config_td), &aue_cfg_init, 0); } } else { if (ifp->if_drv_flags & IFF_DRV_RUNNING) { usbd_config_td_queue_command (&(sc->sc_config_td), &aue_cfg_stop, 0); } } break; case SIOCADDMULTI: case SIOCDELMULTI: usbd_config_td_queue_command (&(sc->sc_config_td), &aue_cfg_setmulti, 0); break; case SIOCGIFMEDIA: case SIOCSIFMEDIA: mii = GET_MII(sc); if (mii == NULL) { error = EINVAL; } else { error = ifmedia_ioctl (ifp, (void *)data, &(mii->mii_media), command); } break; default: error = ether_ioctl(ifp, command, data); break; } mtx_unlock(&(sc->sc_mtx)); return error; } static void aue_watchdog(void *arg) { struct aue_softc *sc = arg; mtx_assert(&(sc->sc_mtx), MA_OWNED); usbd_config_td_queue_command (&(sc->sc_config_td), &aue_cfg_tick, 0); __callout_reset(&(sc->sc_watchdog), hz, &aue_watchdog, sc); mtx_unlock(&(sc->sc_mtx)); return; } /* * Stop the adapter and free any mbufs allocated to the * RX and TX lists. * * NOTE: can be called when "ifp" is NULL */ static void aue_cfg_stop(struct aue_softc *sc, struct aue_config_copy *cc, u_int16_t refcount) { if (cc == NULL) { /* immediate configuration */ struct ifnet *ifp = sc->sc_ifp; if (ifp) { /* clear flags */ ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); } sc->sc_flags &= ~(AUE_FLAG_HL_READY| AUE_FLAG_LL_READY); sc->sc_flags |= AUE_FLAG_WAIT_LINK; /* stop all the transfers, * if not already stopped: */ if (sc->sc_xfer[0]) { usbd_transfer_stop(sc->sc_xfer[0]); } if (sc->sc_xfer[1]) { usbd_transfer_stop(sc->sc_xfer[1]); } if (sc->sc_xfer[2]) { usbd_transfer_stop(sc->sc_xfer[2]); } if (sc->sc_xfer[3]) { usbd_transfer_stop(sc->sc_xfer[3]); } if (sc->sc_xfer[4]) { usbd_transfer_stop(sc->sc_xfer[4]); } if (sc->sc_xfer[5]) { usbd_transfer_stop(sc->sc_xfer[5]); } return; } aue_cfg_csr_write_1(sc, AUE_CTL0, 0); aue_cfg_csr_write_1(sc, AUE_CTL1, 0); aue_cfg_reset(sc); return; } /* * Stop all chip I/O so that the kernel's probe routines don't * get confused by errant DMAs when rebooting. */ static int aue_shutdown(device_t dev) { struct aue_softc *sc = device_get_softc(dev); mtx_lock(&(sc->sc_mtx)); usbd_config_td_queue_command (&(sc->sc_config_td), &aue_cfg_stop, 0); mtx_unlock(&(sc->sc_mtx)); return 0; } pwcbsd/usb/hid.h000644 000423 000000 00000000036 10551670753 014314 0ustar00luigiwheel000000 000000 #include pwcbsd/usb/if_auereg.h000644 000423 000000 00000016607 10551670753 015511 0ustar00luigiwheel000000 000000 /*- * Copyright (c) 1997, 1998, 1999 * Bill Paul . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD: src/sys/dev/usb/if_auereg.h,v 1.22 2005/06/10 16:49:15 brooks Exp $ */ /* * Register definitions for ADMtek Pegasus AN986 USB to Ethernet * chip. The Pegasus uses a total of four USB endpoints: the control * endpoint (0), a bulk read endpoint for receiving packets (1), * a bulk write endpoint for sending packets (2) and an interrupt * endpoint for passing RX and TX status (3). Endpoint 0 is used * to read and write the ethernet module's registers. All registers * are 8 bits wide. * * Packet transfer is done in 64 byte chunks. The last chunk in a * transfer is denoted by having a length less that 64 bytes. For * the RX case, the data includes an optional RX status word. */ #define AUE_UR_READREG 0xF0 #define AUE_UR_WRITEREG 0xF1 #define AUE_CONFIG_NO 1 #define AUE_IFACE_IDX 0 /* * Note that while the ADMtek technically has four endpoints, the control * endpoint (endpoint 0) is regarded as special by the USB code and drivers * don't have direct access to it (we access it using usbd_do_request() * when reading/writing registers. Consequently, our endpoint indexes * don't match those in the ADMtek Pegasus manual: we consider the RX data * endpoint to be index 0 and work up from there. */ #define AUE_ENDPT_MAX 6 #define AUE_INTR_PKTLEN 0x8 #define AUE_CTL0 0x00 #define AUE_CTL1 0x01 #define AUE_CTL2 0x02 #define AUE_MAR0 0x08 #define AUE_MAR1 0x09 #define AUE_MAR2 0x0A #define AUE_MAR3 0x0B #define AUE_MAR4 0x0C #define AUE_MAR5 0x0D #define AUE_MAR6 0x0E #define AUE_MAR7 0x0F #define AUE_MAR AUE_MAR0 #define AUE_PAR0 0x10 #define AUE_PAR1 0x11 #define AUE_PAR2 0x12 #define AUE_PAR3 0x13 #define AUE_PAR4 0x14 #define AUE_PAR5 0x15 #define AUE_PAR AUE_PAR0 #define AUE_PAUSE0 0x18 #define AUE_PAUSE1 0x19 #define AUE_PAUSE AUE_PAUSE0 #define AUE_RX_FLOWCTL_CNT 0x1A #define AUE_RX_FLOWCTL_FIFO 0x1B #define AUE_REG_1D 0x1D #define AUE_EE_REG 0x20 #define AUE_EE_DATA0 0x21 #define AUE_EE_DATA1 0x22 #define AUE_EE_DATA AUE_EE_DATA0 #define AUE_EE_CTL 0x23 #define AUE_PHY_ADDR 0x25 #define AUE_PHY_DATA0 0x26 #define AUE_PHY_DATA1 0x27 #define AUE_PHY_DATA AUE_PHY_DATA0 #define AUE_PHY_CTL 0x28 #define AUE_USB_STS 0x2A #define AUE_TXSTAT0 0x2B #define AUE_TXSTAT1 0x2C #define AUE_TXSTAT AUE_TXSTAT0 #define AUE_RXSTAT 0x2D #define AUE_PKTLOST0 0x2E #define AUE_PKTLOST1 0x2F #define AUE_PKTLOST AUE_PKTLOST0 #define AUE_REG_7B 0x7B #define AUE_GPIO0 0x7E #define AUE_GPIO1 0x7F #define AUE_REG_81 0x81 #define AUE_CTL0_INCLUDE_RXCRC 0x01 #define AUE_CTL0_ALLMULTI 0x02 #define AUE_CTL0_STOP_BACKOFF 0x04 #define AUE_CTL0_RXSTAT_APPEND 0x08 #define AUE_CTL0_WAKEON_ENB 0x10 #define AUE_CTL0_RXPAUSE_ENB 0x20 #define AUE_CTL0_RX_ENB 0x40 #define AUE_CTL0_TX_ENB 0x80 #define AUE_CTL1_HOMELAN 0x04 #define AUE_CTL1_RESETMAC 0x08 #define AUE_CTL1_SPEEDSEL 0x10 /* 0 = 10mbps, 1 = 100mbps */ #define AUE_CTL1_DUPLEX 0x20 /* 0 = half, 1 = full */ #define AUE_CTL1_DELAYHOME 0x40 #define AUE_CTL2_EP3_CLR 0x01 /* reading EP3 clrs status regs */ #define AUE_CTL2_RX_BADFRAMES 0x02 #define AUE_CTL2_RX_PROMISC 0x04 #define AUE_CTL2_LOOPBACK 0x08 #define AUE_CTL2_EEPROMWR_ENB 0x10 #define AUE_CTL2_EEPROM_LOAD 0x20 #define AUE_EECTL_WRITE 0x01 #define AUE_EECTL_READ 0x02 #define AUE_EECTL_DONE 0x04 #define AUE_PHYCTL_PHYREG 0x1F #define AUE_PHYCTL_WRITE 0x20 #define AUE_PHYCTL_READ 0x40 #define AUE_PHYCTL_DONE 0x80 #define AUE_USBSTS_SUSPEND 0x01 #define AUE_USBSTS_RESUME 0x02 #define AUE_TXSTAT0_JABTIMO 0x04 #define AUE_TXSTAT0_CARLOSS 0x08 #define AUE_TXSTAT0_NOCARRIER 0x10 #define AUE_TXSTAT0_LATECOLL 0x20 #define AUE_TXSTAT0_EXCESSCOLL 0x40 #define AUE_TXSTAT0_UNDERRUN 0x80 #define AUE_TXSTAT1_PKTCNT 0x0F #define AUE_TXSTAT1_FIFO_EMPTY 0x40 #define AUE_TXSTAT1_FIFO_FULL 0x80 #define AUE_RXSTAT_OVERRUN 0x01 #define AUE_RXSTAT_PAUSE 0x02 #define AUE_GPIO_IN0 0x01 #define AUE_GPIO_OUT0 0x02 #define AUE_GPIO_SEL0 0x04 #define AUE_GPIO_IN1 0x08 #define AUE_GPIO_OUT1 0x10 #define AUE_GPIO_SEL1 0x20 #define AUE_TIMEOUT 100 /* 10*ms */ #define AUE_MIN_FRAMELEN 60 #define AUE_RXSTAT_MCAST 0x01 #define AUE_RXSTAT_GIANT 0x02 #define AUE_RXSTAT_RUNT 0x04 #define AUE_RXSTAT_CRCERR 0x08 #define AUE_RXSTAT_DRIBBLE 0x10 #define AUE_RXSTAT_MASK 0x1E #define GET_MII(sc) ((sc)->sc_miibus ? \ device_get_softc((sc)->sc_miibus) : NULL) struct aue_intrpkt { uint8_t aue_txstat0; uint8_t aue_txstat1; uint8_t aue_rxstat; uint8_t aue_rxlostpkt0; uint8_t aue_rxlostpkt1; uint8_t aue_wakeupstat; uint8_t aue_rsvd; } __packed; struct aue_rxpkt { uint16_t aue_pktlen; uint8_t aue_rxstat; } __packed; struct aue_softc { struct usbd_config_td sc_config_td; struct __callout sc_watchdog; struct mtx sc_mtx; struct aue_rxpkt sc_rxpkt; struct ifnet *sc_ifp; struct usbd_device *sc_udev; struct usbd_xfer *sc_xfer[AUE_ENDPT_MAX]; device_t sc_miibus; device_t sc_dev; uint32_t sc_unit; uint32_t sc_media_active; uint32_t sc_media_status; uint16_t sc_vendor; uint16_t sc_product; uint16_t sc_flags; #define AUE_FLAG_LSYS 0x0001 /* use Linksys reset */ #define AUE_FLAG_PNA 0x0002 /* has Home PNA */ #define AUE_FLAG_PII 0x0004 /* Pegasus II chip */ #define AUE_FLAG_WAIT_LINK 0x0008 /* wait for link to come up */ #define AUE_FLAG_READ_STALL 0x0010 /* wait for clearing of stall */ #define AUE_FLAG_WRITE_STALL 0x0020 /* wait for clearing of stall */ #define AUE_FLAG_LL_READY 0x0040 /* Lower Layer Ready */ #define AUE_FLAG_HL_READY 0x0080 /* Higher Layer Ready */ #define AUE_FLAG_INTR_STALL 0x0100 /* wait for clearing of stall */ uint8_t sc_name[16]; }; struct aue_config_copy { uint32_t if_flags; uint8_t if_lladdr[ETHER_ADDR_LEN]; uint8_t if_hash[8]; }; pwcbsd/usb/if_axe.c000644 000423 000000 00000072110 10551670753 015000 0ustar00luigiwheel000000 000000 /*- * Copyright (c) 1997, 1998, 1999, 2000-2003 * Bill Paul . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD: src/sys/dev/usb/if_axe.c,v 1.40 2006/09/07 00:06:41 imp Exp $"); /* * ASIX Electronics AX88172 USB 2.0 ethernet driver. Used in the * LinkSys USB200M and various other adapters. * * Manuals available from: * http://www.asix.com.tw/datasheet/mac/Ax88172.PDF * Note: you need the manual for the AX88170 chip (USB 1.x ethernet * controller) to find the definitions for the RX control register. * http://www.asix.com.tw/datasheet/mac/Ax88170.PDF * * Written by Bill Paul * Senior Engineer * Wind River Systems */ /* * The AX88172 provides USB ethernet supports at 10 and 100Mbps. * It uses an external PHY (reference designs use a RealTek chip), * and has a 64-bit multicast hash filter. There is some information * missing from the manual which one needs to know in order to make * the chip function: * * - You must set bit 7 in the RX control register, otherwise the * chip won't receive any packets. * - You must initialize all 3 IPG registers, or you won't be able * to send any packets. * * Note that this device appears to only support loading the station * address via autload from the EEPROM (i.e. there's no way to manaully * set it). * * (Adam Weinberger wanted me to name this driver if_gir.c.) */ /* * NOTE: all function names beginning like "axe_cfg_" can only * be called from within the config thread function ! */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define usbd_config_td_cc axe_config_copy #define usbd_config_td_softc axe_softc #include #include #include #include "usbdevs.h" #include #include /* "device miibus" required. See GENERIC if you get errors here. */ #include "miibus_if.h" #include MODULE_DEPEND(axe, usb, 1, 1, 1); MODULE_DEPEND(axe, ether, 1, 1, 1); MODULE_DEPEND(axe, miibus, 1, 1, 1); #ifdef USB_DEBUG #define DPRINTF(sc,n,fmt,...) \ do { if (axe_debug > (n)) { \ printf("%s:%s: " fmt, (sc)->sc_name, \ __FUNCTION__,## __VA_ARGS__); } } while (0) static int axe_debug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, axe, CTLFLAG_RW, 0, "USB axe"); SYSCTL_INT(_hw_usb_axe, OID_AUTO, debug, CTLFLAG_RW, &axe_debug, 0, "axe debug level"); #else #define DPRINTF(...) #endif /* * Various supported device vendors/products. */ static struct axe_type axe_devs[] = { { USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88172 }, { USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DUBE100 }, { USB_VENDOR_JVC, USB_PRODUCT_JVC_MP_PRX1 }, { USB_VENDOR_LINKSYS2, USB_PRODUCT_LINKSYS2_USB200M }, { USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUAU2KTX }, { USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_FA120 }, { USB_VENDOR_SYSTEMTALKS, USB_PRODUCT_SYSTEMTALKS_SGCX2UL }, { USB_VENDOR_SITECOM, USB_PRODUCT_SITECOM_LN029 }, { 0, 0 } }; static device_probe_t axe_probe; static device_attach_t axe_attach; static device_detach_t axe_detach; static device_shutdown_t axe_shutdown; static void axe_cfg_cmd(struct axe_softc *sc, u_int16_t cmd, u_int16_t index, u_int16_t val, void *buf); static int axe_cfg_miibus_readreg(device_t dev, int phy, int reg); static int axe_cfg_miibus_writereg(device_t dev, int phy, int reg, int val); static void axe_cfg_miibus_statchg(device_t dev); static int axe_ifmedia_upd_cb(struct ifnet *ifp); static void axe_cfg_ifmedia_upd(struct axe_softc *sc, struct axe_config_copy *cc, u_int16_t refcount); static void axe_ifmedia_sts_cb(struct ifnet *ifp, struct ifmediareq *ifmr); static void axe_config_copy(struct axe_softc *sc, struct axe_config_copy *cc, u_int16_t refcount); static void axe_cfg_setmulti(struct axe_softc *sc, struct axe_config_copy *cc, u_int16_t refcount); static void axe_cfg_reset(struct axe_softc *sc); static void axe_cfg_first_time_setup(struct axe_softc *sc, struct axe_config_copy *cc, u_int16_t refcount); static void axe_intr_clear_stall_callback(struct usbd_xfer *xfer); static void axe_intr_callback(struct usbd_xfer *xfer); static void axe_bulk_read_clear_stall_callback(struct usbd_xfer *xfer); static void axe_bulk_read_callback(struct usbd_xfer *xfer); static void axe_bulk_write_clear_stall_callback(struct usbd_xfer *xfer); static void axe_bulk_write_callback(struct usbd_xfer *xfer); static void axe_cfg_tick(struct axe_softc *sc, struct axe_config_copy *cc, u_int16_t refcount); static void axe_start_cb(struct ifnet *ifp); static void axe_start_transfers(struct axe_softc *sc); static void axe_init_cb(void *arg); static void axe_cfg_init(struct axe_softc *sc, struct axe_config_copy *cc, u_int16_t refcount); static void axe_cfg_promisc_upd(struct axe_softc *sc, struct axe_config_copy *cc, u_int16_t refcount); static int axe_ioctl_cb(struct ifnet *ifp, u_long command, caddr_t data); static void axe_watchdog(void *arg); static void axe_cfg_stop(struct axe_softc *sc, struct axe_config_copy *cc, u_int16_t refcount); static const struct usbd_config axe_config[AXE_ENDPT_MAX] = { [0] = { .type = UE_BULK, .endpoint = -1, /* any */ .direction = UE_DIR_OUT, .bufsize = MCLBYTES, .flags = (USBD_USE_DMA|USBD_FORCE_SHORT_XFER), .callback = &axe_bulk_write_callback, .timeout = 10000, /* 10 seconds */ }, [1] = { .type = UE_BULK, .endpoint = -1, /* any */ .direction = UE_DIR_IN, .bufsize = MCLBYTES, .flags = (USBD_USE_DMA|USBD_SHORT_XFER_OK), .callback = &axe_bulk_read_callback, .timeout = 0, /* no timeout */ }, [2] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .flags = USBD_USE_DMA, .callback = &axe_bulk_write_clear_stall_callback, .timeout = 1000, /* 1 second */ }, [3] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .flags = USBD_USE_DMA, .callback = &axe_bulk_read_clear_stall_callback, .timeout = 1000, /* 1 second */ }, [4] = { .type = UE_INTERRUPT, .endpoint = -1, /* any */ .direction = UE_DIR_IN, .flags = USBD_SHORT_XFER_OK, .bufsize = 0, /* use wMaxPacketSize */ .callback = &axe_intr_callback, }, [5] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .flags = USBD_USE_DMA, .callback = &axe_intr_clear_stall_callback, .timeout = 1000, /* 1 second */ }, }; static device_method_t axe_methods[] = { /* Device interface */ DEVMETHOD(device_probe, axe_probe), DEVMETHOD(device_attach, axe_attach), DEVMETHOD(device_detach, axe_detach), DEVMETHOD(device_shutdown, axe_shutdown), /* bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_driver_added, bus_generic_driver_added), /* MII interface */ DEVMETHOD(miibus_readreg, axe_cfg_miibus_readreg), DEVMETHOD(miibus_writereg, axe_cfg_miibus_writereg), DEVMETHOD(miibus_statchg, axe_cfg_miibus_statchg), { 0, 0 } }; static driver_t axe_driver = { .name = "axe", .methods = axe_methods, .size = sizeof(struct axe_softc), }; static devclass_t axe_devclass; DRIVER_MODULE(axe, uhub, axe_driver, axe_devclass, usbd_driver_load, 0); DRIVER_MODULE(miibus, axe, miibus_driver, miibus_devclass, 0, 0); static void axe_cfg_cmd(struct axe_softc *sc, u_int16_t cmd, u_int16_t index, u_int16_t val, void *buf) { usb_device_request_t req; usbd_status err; u_int16_t length = AXE_CMD_LEN(cmd); req.bmRequestType = (AXE_CMD_IS_WRITE(cmd) ? UT_WRITE_VENDOR_DEVICE : UT_READ_VENDOR_DEVICE); req.bRequest = AXE_CMD_CMD(cmd); USETW(req.wValue, val); USETW(req.wIndex, index); USETW(req.wLength, length); if (usbd_config_td_is_gone(&(sc->sc_config_td))) { goto error; } err = usbd_do_request_flags_mtx(sc->sc_udev, &(sc->sc_mtx), &req, buf, 0, NULL, 1000); if (err) { DPRINTF(sc, 0, "device request failed, err=%s " "(ignored)\n", usbd_errstr(err)); error: if ((req.bmRequestType & UT_READ) && length) { bzero(buf, length); } } return; } static int axe_cfg_miibus_readreg(device_t dev, int phy, int reg) { struct axe_softc * sc = device_get_softc(dev); u_int16_t val; mtx_lock(&(sc->sc_mtx)); /* XXX */ #if 0 /* * The chip tells us the MII address of any supported * PHYs attached to the chip, so only read from those. */ if ((sc->sc_phyaddrs[0] != AXE_NOPHY) && (phy != sc->sc_phyaddrs[0])) { val = 0; goto done; } if ((sc->sc_phyaddrs[1] != AXE_NOPHY) && (phy != sc->sc_phyaddrs[1])) { val = 0; goto done; } #endif if ((sc->sc_phyaddrs[0] != 0xFF) && (sc->sc_phyaddrs[0] != phy)) { val = 0; goto done; } axe_cfg_cmd(sc, AXE_CMD_MII_OPMODE_SW, 0, 0, NULL); axe_cfg_cmd(sc, AXE_CMD_MII_READ_REG, reg, phy, &val); axe_cfg_cmd(sc, AXE_CMD_MII_OPMODE_HW, 0, 0, NULL); val = le16toh(val); if (val) { sc->sc_phyaddrs[0] = phy; } done: mtx_unlock(&(sc->sc_mtx)); /* XXX */ return (val); } static int axe_cfg_miibus_writereg(device_t dev, int phy, int reg, int val) { struct axe_softc * sc = device_get_softc(dev); val = htole16(val); mtx_lock(&(sc->sc_mtx)); /* XXX */ axe_cfg_cmd(sc, AXE_CMD_MII_OPMODE_SW, 0, 0, NULL); axe_cfg_cmd(sc, AXE_CMD_MII_WRITE_REG, reg, phy, &val); axe_cfg_cmd(sc, AXE_CMD_MII_OPMODE_HW, 0, 0, NULL); mtx_unlock(&(sc->sc_mtx)); /* XXX */ return 0; } static void axe_cfg_miibus_statchg(device_t dev) { /* doesn't seem to be necessary */ return; } /* * Set media options. */ static int axe_ifmedia_upd_cb(struct ifnet *ifp) { struct axe_softc *sc = ifp->if_softc; mtx_lock(&(sc->sc_mtx)); usbd_config_td_queue_command (&(sc->sc_config_td), &axe_cfg_ifmedia_upd, 0); mtx_unlock(&(sc->sc_mtx)); return 0; } static void axe_cfg_ifmedia_upd(struct axe_softc *sc, struct axe_config_copy *cc, u_int16_t refcount) { struct ifnet * ifp = sc->sc_ifp; struct mii_data * mii = GET_MII(sc); if ((cc == NULL) || (ifp == NULL) || (mii == NULL)) { /* not ready */ return; } sc->sc_flags |= AXE_FLAG_WAIT_LINK; if (mii->mii_instance) { struct mii_softc *miisc; LIST_FOREACH(miisc, &mii->mii_phys, mii_list) { mii_phy_reset(miisc); } } mii_mediachg(mii); return; } /* * Report current media status. */ static void axe_ifmedia_sts_cb(struct ifnet *ifp, struct ifmediareq *ifmr) { struct axe_softc *sc = ifp->if_softc; mtx_lock(&(sc->sc_mtx)); ifmr->ifm_active = sc->sc_media_active; ifmr->ifm_status = sc->sc_media_status; mtx_unlock(&(sc->sc_mtx)); return; } static void axe_config_copy(struct axe_softc *sc, struct axe_config_copy *cc, u_int16_t refcount) { struct ifnet * ifp = sc->sc_ifp; struct ifmultiaddr * ifma; u_int8_t h; u_int8_t i; bzero(cc, sizeof(*cc)); if (ifp) { for (i = 0; i < ETHER_ADDR_LEN; i++) { cc->if_lladdr[i] = IF_LLADDR(ifp)[i]; } cc->if_flags = ifp->if_flags; /* compute hash bits for multicast filter */ IF_ADDR_LOCK(ifp); TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) { continue; } h = (ether_crc32_be (LLADDR((struct sockaddr_dl *)(ifma->ifma_addr)), ETHER_ADDR_LEN) >> 26); cc->if_hash[(h >> 3)] |= (1 << (h & 7)); } IF_ADDR_UNLOCK(ifp); } return; } static void axe_cfg_setmulti(struct axe_softc *sc, struct axe_config_copy *cc, u_int16_t refcount) { u_int16_t rxmode; if (cc == NULL) { /* nothing to do */ return; } axe_cfg_cmd(sc, AXE_CMD_RXCTL_READ, 0, 0, &rxmode); rxmode = le16toh(rxmode); if (cc->if_flags & (IFF_ALLMULTI|IFF_PROMISC)) { rxmode |= AXE_RXCMD_ALLMULTI; axe_cfg_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL); return; } rxmode &= ~AXE_RXCMD_ALLMULTI; axe_cfg_cmd(sc, AXE_CMD_WRITE_MCAST, 0, 0, cc->if_hash); axe_cfg_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL); return; } static void axe_cfg_reset(struct axe_softc *sc) { usbd_status err; mtx_unlock(&(sc->sc_mtx)); mtx_lock(&Giant); err = usbreq_set_config(sc->sc_udev, AXE_CONFIG_NO); mtx_unlock(&Giant); mtx_lock(&(sc->sc_mtx)); if (err) { DPRINTF(sc, 0, "reset failed (ignored)\n"); } /* wait a little while for the * chip to get its brains in order: */ err = usbd_config_td_sleep(&(sc->sc_config_td), hz/100); return; } /* * Probe for a AX88172 chip. */ static int axe_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct axe_type *t; if (uaa->iface != NULL) { return UMATCH_NONE; } t = axe_devs; while(t->axe_vid) { if ((uaa->vendor == t->axe_vid) && (uaa->product == t->axe_did)) { return UMATCH_VENDOR_PRODUCT; } t++; } return UMATCH_NONE; } /* * Attach the interface. Allocate softc structures, do ifmedia * setup and ethernet/BPF attach. */ static int axe_attach(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct axe_softc *sc = device_get_softc(dev); int32_t error; if (sc == NULL) { return ENOMEM; } sc->sc_udev = uaa->device; sc->sc_dev = dev; sc->sc_unit = device_get_unit(dev); usbd_set_desc(dev, uaa->device); snprintf(sc->sc_name, sizeof(sc->sc_name), "%s", device_get_nameunit(dev)); mtx_init(&(sc->sc_mtx), "axe lock", NULL, MTX_DEF | MTX_RECURSE); __callout_init_mtx(&(sc->sc_watchdog), &(sc->sc_mtx), CALLOUT_RETURNUNLOCKED); error = usbd_set_config_no(uaa->device, AXE_CONFIG_NO, 1); if (error) { device_printf(dev, "setting config " "number failed!\n"); goto detach; } error = usbd_transfer_setup(uaa->device, AXE_IFACE_IDX, sc->sc_xfer, axe_config, AXE_ENDPT_MAX, sc, &(sc->sc_mtx)); if (error) { device_printf(dev, "allocating USB " "transfers failed!\n"); goto detach; } error = usbd_config_td_setup(&(sc->sc_config_td), sc, &(sc->sc_mtx), &axe_config_copy, NULL, sizeof(struct axe_config_copy), 16); if (error) { device_printf(dev, "could not setup config " "thread!\n"); goto detach; } mtx_lock(&(sc->sc_mtx)); sc->sc_flags |= AXE_FLAG_WAIT_LINK; /* start setup */ usbd_config_td_queue_command (&(sc->sc_config_td), &axe_cfg_first_time_setup, 0); /* start watchdog (will exit mutex) */ axe_watchdog(sc); return 0; /* success */ detach: axe_detach(dev); return ENXIO; /* failure */ } static void axe_cfg_first_time_setup(struct axe_softc *sc, struct axe_config_copy *cc, u_int16_t refcount) { struct ifnet * ifp; int error; u_int8_t eaddr[min(ETHER_ADDR_LEN,6)]; if (cc == NULL) { return; } /* set default value */ bzero(eaddr, sizeof(eaddr)); /* * Get station address. */ axe_cfg_cmd(sc, AXE_CMD_READ_NODEID, 0, 0, eaddr); /* * Load IPG values and PHY indexes. */ axe_cfg_cmd(sc, AXE_CMD_READ_IPG012, 0, 0, sc->sc_ipgs); axe_cfg_cmd(sc, AXE_CMD_READ_PHYID, 0, 0, sc->sc_phyaddrs); /* * Work around broken adapters that appear to lie about * their PHY addresses. */ sc->sc_phyaddrs[0] = sc->sc_phyaddrs[1] = 0xFF; mtx_unlock(&(sc->sc_mtx)); ifp = if_alloc(IFT_ETHER); mtx_lock(&(sc->sc_mtx)); if (ifp == NULL) { printf("%s: could not if_alloc()\n", sc->sc_name); goto done; } ifp->if_softc = sc; if_initname(ifp, "axe", sc->sc_unit); ifp->if_mtu = ETHERMTU; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = axe_ioctl_cb; ifp->if_start = axe_start_cb; ifp->if_watchdog = NULL; ifp->if_init = axe_init_cb; ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; /* XXX need Giant when accessing * the device structures ! */ mtx_unlock(&(sc->sc_mtx)); mtx_lock(&Giant); error = mii_phy_probe(sc->sc_dev, &(sc->sc_miibus), &axe_ifmedia_upd_cb, &axe_ifmedia_sts_cb); mtx_unlock(&Giant); mtx_lock(&(sc->sc_mtx)); if (error) { printf("%s: MII without any PHY!\n", sc->sc_name); if_free(ifp); goto done; } sc->sc_ifp = ifp; /* * Call MI attach routine. */ ether_ifattach(ifp, eaddr); done: return; } static int axe_detach(device_t dev) { struct axe_softc * sc = device_get_softc(dev); struct ifnet * ifp; mtx_lock(&(sc->sc_mtx)); usbd_config_td_stop(&(sc->sc_config_td)); __callout_stop(&sc->sc_watchdog); axe_cfg_stop(sc, NULL, 0); ifp = sc->sc_ifp; mtx_unlock(&(sc->sc_mtx)); /* get rid of any late children */ bus_generic_detach(dev); if (ifp) { ether_ifdetach(ifp); if_free(ifp); } usbd_transfer_unsetup(sc->sc_xfer, AXE_ENDPT_MAX); usbd_config_td_unsetup(&(sc->sc_config_td)); __callout_drain(&(sc->sc_watchdog)); mtx_destroy(&(sc->sc_mtx)); return 0; } static void axe_intr_clear_stall_callback(struct usbd_xfer *xfer) { struct axe_softc *sc = xfer->priv_sc; struct usbd_xfer *xfer_other = sc->sc_xfer[4]; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, xfer_other); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, xfer_other); sc->sc_flags &= ~AXE_FLAG_INTR_STALL; usbd_transfer_start(xfer_other); return; tr_error: /* bomb out */ sc->sc_flags &= ~AXE_FLAG_INTR_STALL; DPRINTF(sc, 0, "interrupt read pipe stopped\n"); return; } static void axe_intr_callback(struct usbd_xfer *xfer) { struct axe_softc *sc = xfer->priv_sc; USBD_CHECK_STATUS(xfer); tr_transferred: /* do nothing */ tr_setup: if (sc->sc_flags & AXE_FLAG_INTR_STALL) { usbd_transfer_start(sc->sc_xfer[5]); } else { usbd_start_hardware(xfer); } return; tr_error: if (xfer->error != USBD_CANCELLED) { /* start clear stall */ sc->sc_flags |= AXE_FLAG_INTR_STALL; usbd_transfer_start(sc->sc_xfer[5]); } return; } static void axe_bulk_read_clear_stall_callback(struct usbd_xfer *xfer) { struct axe_softc *sc = xfer->priv_sc; struct usbd_xfer *xfer_other = sc->sc_xfer[1]; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, xfer_other); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, xfer_other); sc->sc_flags &= ~AXE_FLAG_READ_STALL; usbd_transfer_start(xfer_other); return; tr_error: /* bomb out */ sc->sc_flags &= ~AXE_FLAG_READ_STALL; DPRINTF(sc, 0, "bulk read pipe stopped\n"); return; } static void axe_bulk_read_callback(struct usbd_xfer *xfer) { struct axe_softc *sc = xfer->priv_sc; struct ifnet *ifp = sc->sc_ifp; struct mbuf *m; USBD_CHECK_STATUS(xfer); tr_error: if (xfer->error != USBD_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= AXE_FLAG_READ_STALL; usbd_transfer_start(sc->sc_xfer[3]); } DPRINTF(sc, 0, "bulk read error, %s\n", usbd_errstr(xfer->error)); return; tr_transferred: if (xfer->actlen < sizeof(struct ether_header)) { ifp->if_ierrors++; goto tr_setup; } m = usbd_ether_get_mbuf(); if (m == NULL) { ifp->if_ierrors++; goto tr_setup; } xfer->actlen = min(xfer->actlen, m->m_len); usbd_copy_out(&(xfer->buf_data), 0, m->m_data, xfer->actlen); ifp->if_ipackets++; m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = m->m_len = xfer->actlen; (ifp->if_input)(ifp, m); tr_setup: if (sc->sc_flags & AXE_FLAG_READ_STALL) { usbd_transfer_start(sc->sc_xfer[3]); } else { usbd_start_hardware(xfer); } return; } static void axe_bulk_write_clear_stall_callback(struct usbd_xfer *xfer) { struct axe_softc *sc = xfer->priv_sc; struct usbd_xfer *xfer_other = sc->sc_xfer[0]; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, xfer_other); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, xfer_other); sc->sc_flags &= ~AXE_FLAG_WRITE_STALL; usbd_transfer_start(xfer_other); return; tr_error: /* bomb out */ sc->sc_flags &= ~AXE_FLAG_WRITE_STALL; DPRINTF(sc, 0, "bulk write pipe stopped\n"); return; } static void axe_bulk_write_callback(struct usbd_xfer *xfer) { struct axe_softc *sc = xfer->priv_sc; struct ifnet *ifp = sc->sc_ifp; struct mbuf *m; USBD_CHECK_STATUS(xfer); tr_error: DPRINTF(sc, 10, "transfer error, %s\n", usbd_errstr(xfer->error)); if (xfer->error != USBD_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= AXE_FLAG_WRITE_STALL; usbd_transfer_start(sc->sc_xfer[2]); } ifp->if_oerrors++; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; return; tr_transferred: DPRINTF(sc, 10, "transfer complete\n"); ifp->if_opackets++; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; tr_setup: if (sc->sc_flags & AXE_FLAG_WRITE_STALL) { usbd_transfer_start(sc->sc_xfer[2]); goto done; } if (sc->sc_flags & AXE_FLAG_WAIT_LINK) { /* don't send anything * if there is no link ! */ goto done; } IF_DEQUEUE(&(ifp->if_snd), m); if (m == NULL) { goto done; } if (m->m_pkthdr.len > MCLBYTES) { m->m_pkthdr.len = MCLBYTES; } xfer->length = m->m_pkthdr.len; usbd_m_copy_in(&(xfer->buf_data), 0, m, 0, m->m_pkthdr.len); /* * if there's a BPF listener, bounce a copy * of this frame to him: */ BPF_MTAP(ifp, m); m_freem(m); usbd_start_hardware(xfer); ifp->if_drv_flags |= IFF_DRV_OACTIVE; done: return; } static void axe_cfg_tick(struct axe_softc *sc, struct axe_config_copy *cc, u_int16_t refcount) { struct ifnet * ifp = sc->sc_ifp; struct mii_data * mii = GET_MII(sc); if ((cc == NULL) || (ifp == NULL) || (mii == NULL)) { /* not ready */ return; } mii_tick(mii); mii_pollstat(mii); if ((sc->sc_flags & AXE_FLAG_WAIT_LINK) && (mii->mii_media_status & IFM_ACTIVE) && (IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE)) { sc->sc_flags &= ~AXE_FLAG_WAIT_LINK; } sc->sc_media_active = mii->mii_media_active; sc->sc_media_status = mii->mii_media_status; /* start stopped transfers, if any */ axe_start_transfers(sc); return; } static void axe_start_cb(struct ifnet *ifp) { struct axe_softc *sc = ifp->if_softc; mtx_lock(&(sc->sc_mtx)); axe_start_transfers(sc); mtx_unlock(&(sc->sc_mtx)); return; } static void axe_start_transfers(struct axe_softc *sc) { if ((sc->sc_flags & AXE_FLAG_LL_READY) && (sc->sc_flags & AXE_FLAG_HL_READY)) { /* start the USB transfers, * if not already started: */ usbd_transfer_start(sc->sc_xfer[4]); usbd_transfer_start(sc->sc_xfer[1]); usbd_transfer_start(sc->sc_xfer[0]); } return; } static void axe_init_cb(void *arg) { struct axe_softc *sc = arg; mtx_lock(&(sc->sc_mtx)); usbd_config_td_queue_command (&(sc->sc_config_td), &axe_cfg_init, 0); mtx_unlock(&(sc->sc_mtx)); return; } static void axe_cfg_init(struct axe_softc *sc, struct axe_config_copy *cc, u_int16_t refcount) { struct mii_data *mii = GET_MII(sc); u_int16_t rxmode; if (cc == NULL) { /* immediate configuration */ struct ifnet *ifp = sc->sc_ifp; axe_cfg_stop(sc, NULL, 0); ifp->if_drv_flags |= IFF_DRV_RUNNING; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; sc->sc_flags |= AXE_FLAG_HL_READY; return; } /* * Cancel pending I/O */ axe_cfg_stop(sc, cc, 0); #if 0 /* Set MAC address */ axe_mac(sc, cc->if_lladdr); #endif /* Set transmitter IPG values */ axe_cfg_cmd(sc, AXE_CMD_WRITE_IPG0, 0, sc->sc_ipgs[0], NULL); axe_cfg_cmd(sc, AXE_CMD_WRITE_IPG1, 0, sc->sc_ipgs[1], NULL); axe_cfg_cmd(sc, AXE_CMD_WRITE_IPG2, 0, sc->sc_ipgs[2], NULL); /* Enable receiver, set RX mode */ rxmode = (AXE_RXCMD_UNICAST|AXE_RXCMD_MULTICAST|AXE_RXCMD_ENABLE); /* If we want promiscuous mode, set the allframes bit. */ if (cc->if_flags & IFF_PROMISC) { rxmode |= AXE_RXCMD_PROMISC; } if (cc->if_flags & IFF_BROADCAST) { rxmode |= AXE_RXCMD_BROADCAST; } axe_cfg_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL); /* Load the multicast filter. */ axe_cfg_setmulti(sc, cc, 0); mii_mediachg(mii); sc->sc_flags |= (AXE_FLAG_READ_STALL| AXE_FLAG_WRITE_STALL| AXE_FLAG_LL_READY); axe_start_transfers(sc); return; } static void axe_cfg_promisc_upd(struct axe_softc *sc, struct axe_config_copy *cc, u_int16_t refcount) { u_int16_t rxmode; if (cc == NULL) { /* nothing to do */ return; } axe_cfg_cmd(sc, AXE_CMD_RXCTL_READ, 0, 0, &rxmode); rxmode = le16toh(rxmode); if (cc->if_flags & IFF_PROMISC) { rxmode |= AXE_RXCMD_PROMISC; } else { rxmode &= ~AXE_RXCMD_PROMISC; } axe_cfg_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL); axe_cfg_setmulti(sc, cc, 0); return; } static int axe_ioctl_cb(struct ifnet *ifp, u_long command, caddr_t data) { struct axe_softc * sc = ifp->if_softc; struct mii_data * mii; int error = 0; mtx_lock(&(sc->sc_mtx)); switch(command) { case SIOCSIFFLAGS: if (ifp->if_flags & IFF_UP) { if (ifp->if_drv_flags & IFF_DRV_RUNNING) { usbd_config_td_queue_command (&(sc->sc_config_td), &axe_cfg_promisc_upd, 0); } else { usbd_config_td_queue_command (&(sc->sc_config_td), &axe_cfg_init, 0); } } else { if (ifp->if_drv_flags & IFF_DRV_RUNNING) { usbd_config_td_queue_command (&(sc->sc_config_td), &axe_cfg_stop, 0); } } break; case SIOCADDMULTI: case SIOCDELMULTI: usbd_config_td_queue_command (&(sc->sc_config_td), &axe_cfg_setmulti, 0); break; case SIOCGIFMEDIA: case SIOCSIFMEDIA: mii = GET_MII(sc); if (mii == NULL) { error = EINVAL; } else { error = ifmedia_ioctl (ifp, (void *)data, &(mii->mii_media), command); } break; default: error = ether_ioctl(ifp, command, data); break; } mtx_unlock(&(sc->sc_mtx)); return error; } static void axe_watchdog(void *arg) { struct axe_softc *sc = arg; mtx_assert(&(sc->sc_mtx), MA_OWNED); usbd_config_td_queue_command (&(sc->sc_config_td), &axe_cfg_tick, 0); __callout_reset(&(sc->sc_watchdog), hz, &axe_watchdog, sc); mtx_unlock(&(sc->sc_mtx)); return; } /* * NOTE: can be called when "ifp" is NULL */ static void axe_cfg_stop(struct axe_softc *sc, struct axe_config_copy *cc, u_int16_t refcount) { if (cc == NULL) { /* immediate configuration */ struct ifnet *ifp = sc->sc_ifp; if (ifp) { /* clear flags */ ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); } sc->sc_flags &= ~(AXE_FLAG_HL_READY| AXE_FLAG_LL_READY); sc->sc_flags |= AXE_FLAG_WAIT_LINK; /* stop all the transfers, * if not already stopped: */ if (sc->sc_xfer[0]) { usbd_transfer_stop(sc->sc_xfer[0]); } if (sc->sc_xfer[1]) { usbd_transfer_stop(sc->sc_xfer[1]); } if (sc->sc_xfer[2]) { usbd_transfer_stop(sc->sc_xfer[2]); } if (sc->sc_xfer[3]) { usbd_transfer_stop(sc->sc_xfer[3]); } if (sc->sc_xfer[4]) { usbd_transfer_stop(sc->sc_xfer[4]); } if (sc->sc_xfer[5]) { usbd_transfer_stop(sc->sc_xfer[5]); } return; } axe_cfg_reset(sc); return; } /* * Stop all chip I/O so that the kernel's probe routines don't * get confused by errant DMAs when rebooting. */ static int axe_shutdown(device_t dev) { struct axe_softc *sc = device_get_softc(dev); mtx_lock(&(sc->sc_mtx)); usbd_config_td_queue_command (&(sc->sc_config_td), &axe_cfg_stop, 0); mtx_unlock(&(sc->sc_mtx)); return 0; } pwcbsd/usb/if_axereg.h000644 000423 000000 00000012062 10551670753 015503 0ustar00luigiwheel000000 000000 /*- * Copyright (c) 1997, 1998, 1999, 2000-2003 * Bill Paul . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD: src/sys/dev/usb/if_axereg.h,v 1.10 2006/06/04 14:42:38 iedowse Exp $ */ /* * Definitions for the ASIX Electronics AX88172 to ethernet controller. */ /* * Vendor specific commands. ASIX conveniently doesn't document the 'set * NODEID' command in their datasheet (thanks a lot guys). * To make handling these commands easier, I added some extra data which is * decided by the axe_cmd() routine. Commands are encoded in 16 bytes, with * the format: LDCC. L and D are both nibbles in the high byte. L represents * the data length (0 to 15) and D represents the direction (0 for vendor read, * 1 for vendor write). CC is the command byte, as specified in the manual. */ #define AXE_CMD_IS_WRITE(x) (((x) & 0x0F00) >> 8) #define AXE_CMD_LEN(x) (((x) & 0xF000) >> 12) #define AXE_CMD_CMD(x) ((x) & 0x00FF) #define AXE_CMD_READ_RXTX_SRAM 0x2002 #define AXE_CMD_WRITE_RX_SRAM 0x0103 #define AXE_CMD_WRITE_TX_SRAM 0x0104 #define AXE_CMD_MII_OPMODE_SW 0x0106 #define AXE_CMD_MII_READ_REG 0x2007 #define AXE_CMD_MII_WRITE_REG 0x2108 #define AXE_CMD_MII_READ_OPMODE 0x1009 #define AXE_CMD_MII_OPMODE_HW 0x010A #define AXE_CMD_SROM_READ 0x200B #define AXE_CMD_SROM_WRITE 0x010C #define AXE_CMD_SROM_WR_ENABLE 0x010D #define AXE_CMD_SROM_WR_DISABLE 0x010E #define AXE_CMD_RXCTL_READ 0x200F #define AXE_CMD_RXCTL_WRITE 0x0110 #define AXE_CMD_READ_IPG012 0x3011 #define AXE_CMD_WRITE_IPG0 0x0112 #define AXE_CMD_WRITE_IPG1 0x0113 #define AXE_CMD_WRITE_IPG2 0x0114 #define AXE_CMD_READ_MCAST 0x8015 #define AXE_CMD_WRITE_MCAST 0x8116 #define AXE_CMD_READ_NODEID 0x6017 #define AXE_CMD_WRITE_NODEID 0x6118 #define AXE_CMD_READ_PHYID 0x2019 #define AXE_CMD_READ_MEDIA 0x101A #define AXE_CMD_WRITE_MEDIA 0x011B #define AXE_CMD_READ_MONITOR_MODE 0x101C #define AXE_CMD_WRITE_MONITOR_MODE 0x011D #define AXE_CMD_READ_GPIO 0x101E #define AXE_CMD_WRITE_GPIO 0x011F #define AXE_RXCMD_PROMISC 0x0001 #define AXE_RXCMD_ALLMULTI 0x0002 #define AXE_RXCMD_UNICAST 0x0004 #define AXE_RXCMD_BROADCAST 0x0008 #define AXE_RXCMD_MULTICAST 0x0010 #define AXE_RXCMD_ENABLE 0x0080 #define AXE_NOPHY 0xE0 #define AXE_TIMEOUT 1000 #define AXE_MIN_FRAMELEN 60 #define AXE_RX_FRAMES 1 #define AXE_TX_FRAMES 1 #define AXE_CTL_READ 0x01 #define AXE_CTL_WRITE 0x02 #define AXE_CONFIG_NO 1 #define AXE_IFACE_IDX 0 /* The interrupt endpoint is currently unused by the ASIX part. */ #define AXE_ENDPT_MAX 6 struct axe_type { uint16_t axe_vid; uint16_t axe_did; }; #define GET_MII(sc) ((sc)->sc_miibus ? \ device_get_softc((sc)->sc_miibus) : NULL) struct axe_softc { struct usbd_config_td sc_config_td; struct __callout sc_watchdog; struct mtx sc_mtx; struct ifnet *sc_ifp; struct usbd_device *sc_udev; struct usbd_xfer *sc_xfer[AXE_ENDPT_MAX]; device_t sc_miibus; device_t sc_dev; uint32_t sc_unit; uint32_t sc_media_active; uint32_t sc_media_status; uint16_t sc_flags; #define AXE_FLAG_WAIT_LINK 0x0001 #define AXE_FLAG_INTR_STALL 0x0002 #define AXE_FLAG_READ_STALL 0x0004 #define AXE_FLAG_WRITE_STALL 0x0008 #define AXE_FLAG_LL_READY 0x0010 #define AXE_FLAG_HL_READY 0x0020 uint8_t sc_ipgs[3]; uint8_t sc_phyaddrs[2]; uint8_t sc_name[16]; }; struct axe_config_copy { uint32_t if_flags; uint8_t if_lladdr[ETHER_ADDR_LEN]; uint8_t if_hash[8]; }; pwcbsd/usb/if_cdce.c000644 000423 000000 00000045175 10551670753 015134 0ustar00luigiwheel000000 000000 /* $NetBSD: if_cdce.c,v 1.4 2004/10/24 12:50:54 augustss Exp $ */ /* * Copyright (c) 1997, 1998, 1999, 2000-2003 Bill Paul * Copyright (c) 2003-2005 Craig Boston * Copyright (c) 2004 Daniel Hartmeier * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul, THE VOICES IN HIS HEAD OR * THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * USB Communication Device Class (Ethernet Networking Control Model) * http://www.usb.org/developers/devclass_docs/usbcdc11.pdf */ #include __FBSDID("$FreeBSD: src/sys/dev/usb/if_cdce.c,v 1.14 2006/09/07 00:06:41 imp Exp $"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" #include static device_probe_t cdce_probe; static device_attach_t cdce_attach; static device_detach_t cdce_detach; static device_shutdown_t cdce_shutdown; static void cdce_start_cb(struct ifnet *ifp); static void cdce_start_transfers(struct cdce_softc *sc); static void cdce_bulk_write_clear_stall_callback(struct usbd_xfer *xfer); static void cdce_bulk_write_callback(struct usbd_xfer *xfer); static u_int32_t cdce_m_crc32(struct mbuf *m, u_int32_t src_offset, u_int32_t src_len); static void cdce_stop(struct cdce_softc *sc); static int cdce_ioctl_cb(struct ifnet *ifp, u_long command, caddr_t data); static void cdce_init_cb(void *arg); static void cdce_bulk_read_clear_stall_callback(struct usbd_xfer *xfer); static void cdce_bulk_read_callback(struct usbd_xfer *xfer); static int cdce_ifmedia_upd_cb(struct ifnet *ifp); static void cdce_ifmedia_sts_cb(struct ifnet * const ifp, struct ifmediareq *req); #define DPRINTF(...) static const struct usbd_config cdce_config[CDCE_ENDPT_MAX] = { [0] = { .type = UE_BULK, .endpoint = -1, /* any */ .direction = UE_DIR_OUT, .bufsize = (MCLBYTES + 4), .flags = (USBD_USE_DMA|USBD_FORCE_SHORT_XFER), .callback = &cdce_bulk_write_callback, .timeout = 10000, /* 10 seconds */ }, [1] = { .type = UE_BULK, .endpoint = -1, /* any */ .direction = UE_DIR_IN, .bufsize = (MCLBYTES + 4), .flags = (USBD_USE_DMA|USBD_SHORT_XFER_OK), .callback = &cdce_bulk_read_callback, }, [2] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .flags = USBD_USE_DMA, .callback = &cdce_bulk_write_clear_stall_callback, .timeout = 1000, /* 1 second */ }, [3] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .flags = USBD_USE_DMA, .callback = &cdce_bulk_read_clear_stall_callback, .timeout = 1000, /* 1 second */ }, }; static device_method_t cdce_methods[] = { /* Device interface */ DEVMETHOD(device_probe, cdce_probe), DEVMETHOD(device_attach, cdce_attach), DEVMETHOD(device_detach, cdce_detach), DEVMETHOD(device_shutdown, cdce_shutdown), { 0, 0 } }; static driver_t cdce_driver = { .name = "cdce", .methods = cdce_methods, .size = sizeof(struct cdce_softc), }; static devclass_t cdce_devclass; DRIVER_MODULE(cdce, uhub, cdce_driver, cdce_devclass, usbd_driver_load, 0); MODULE_VERSION(cdce, 0); MODULE_DEPEND(cdce, usb, 1, 1, 1); MODULE_DEPEND(cdce, ether, 1, 1, 1); static const struct cdce_type cdce_devs[] = { {{ USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2501 }, CDCE_FLAG_NO_UNION }, {{ USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SL5500 }, CDCE_FLAG_ZAURUS }, {{ USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SLA300 }, CDCE_FLAG_ZAURUS | CDCE_FLAG_NO_UNION }, {{ USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SL5600 }, CDCE_FLAG_ZAURUS | CDCE_FLAG_NO_UNION }, {{ USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SLC700 }, CDCE_FLAG_ZAURUS | CDCE_FLAG_NO_UNION }, {{ USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SLC750 }, CDCE_FLAG_ZAURUS | CDCE_FLAG_NO_UNION }, {{ USB_VENDOR_GMATE, USB_PRODUCT_GMATE_YP3X00 }, CDCE_FLAG_NO_UNION }, {{ USB_VENDOR_NETCHIP, USB_PRODUCT_NETCHIP_ETHERNETGADGET }, CDCE_FLAG_NO_UNION }, }; #define cdce_lookup(v, p) ((const struct cdce_type *)usb_lookup(cdce_devs, v, p)) static int cdce_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); usb_interface_descriptor_t *id; if (uaa->iface == NULL) { return UMATCH_NONE; } id = usbd_get_interface_descriptor(uaa->iface); if (id == NULL) { return UMATCH_NONE; } if (cdce_lookup(uaa->vendor, uaa->product) != NULL) { return UMATCH_VENDOR_PRODUCT; } if ((id->bInterfaceClass == UICLASS_CDC) && (id->bInterfaceSubClass == UISUBCLASS_ETHERNET_NETWORKING_CONTROL_MODEL)) { return UMATCH_IFACECLASS_GENERIC; } return UMATCH_NONE; } static int cdce_attach(device_t dev) { struct cdce_softc * sc = device_get_softc(dev); struct usb_attach_arg * uaa = device_get_ivars(dev); struct usbd_interface * iface; const usb_cdc_union_descriptor_t * ud; const usb_cdc_ethernet_descriptor_t * ue; const usb_interface_descriptor_t *id; const struct cdce_type * t; struct ifnet * ifp; int error; u_int8_t i; u_int8_t eaddr[ETHER_ADDR_LEN]; u_int8_t eaddr_str[(ETHER_ADDR_LEN * 2) + 1]; if (sc == NULL) { return ENOMEM; } sc->sc_udev = uaa->device; sc->sc_dev = dev; sc->sc_unit = device_get_unit(dev); t = cdce_lookup(uaa->vendor, uaa->product); if (t) { sc->sc_flags = t->cdce_flags; } usbd_set_desc(dev, uaa->device); snprintf(sc->sc_name, sizeof(sc->sc_name), "%s", device_get_nameunit(dev)); mtx_init(&(sc->sc_mtx), "cdce lock", NULL, MTX_DEF | MTX_RECURSE); if (sc->sc_flags & CDCE_FLAG_NO_UNION) { sc->sc_data_iface_index = uaa->iface_index; sc->sc_data_iface_no = 0; /* not used */ goto alloc_transfers; } ud = ((const void *)usbd_find_descriptor (usbd_get_config_descriptor(uaa->device), UDESC_CS_INTERFACE, UDESCSUB_CDC_UNION)); if ((ud == NULL) || (ud->bLength < sizeof(*ud))) { device_printf(dev, "no union descriptor!\n"); goto detach; } sc->sc_data_iface_no = ud->bSlaveInterface[0]; for (i=0; ; i++) { iface = usbd_get_iface(uaa->device, i); if (iface) { id = usbd_get_interface_descriptor(iface); if (id && (id->bInterfaceNumber == sc->sc_data_iface_no)) { sc->sc_data_iface_index = i; USBD_SET_IFACE_NO_PROBE(uaa->device, i); break; } } else { device_printf(dev, "no data interface found!\n"); goto detach; } } /* * * The Data Class interface of a networking device shall have a minimum * of two interface settings. The first setting (the default interface * setting) includes no endpoints and therefore no networking traffic is * exchanged whenever the default interface setting is selected. One or * more additional interface settings are used for normal operation, and * therefore each includes a pair of endpoints (one IN, and one OUT) to * exchange network traffic. Select an alternate interface setting to * initialize the network aspects of the device and to enable the * exchange of network traffic. * * * Some devices, most notably cable modems, include interface settings * that have no IN or OUT endpoint, therefore loop through the list of all * available interface settings looking for one with both IN and OUT * endpoints. */ alloc_transfers: for (i = 0; i < 32; i++) { error = usbreq_set_interface (uaa->device, sc->sc_data_iface_index, i); if (error) { device_printf(dev, "no valid alternate setting found!\n"); goto detach; } error = usbd_transfer_setup (uaa->device, sc->sc_data_iface_index, sc->sc_xfer, cdce_config, CDCE_ENDPT_MAX, sc, &(sc->sc_mtx)); if (error == 0) { break; } } ifmedia_init(&(sc->sc_ifmedia), 0, &cdce_ifmedia_upd_cb, &cdce_ifmedia_sts_cb); ue = ((const void *)usbd_find_descriptor (usbd_get_config_descriptor(uaa->device), UDESC_INTERFACE, UDESCSUB_CDC_ENF)); if ((ue == NULL) || (ue->bLength < sizeof(*ue)) || usbreq_get_string_any(uaa->device, ue->iMacAddress, eaddr_str, sizeof(eaddr_str))) { /* fake MAC address */ device_printf(dev, "faking MAC address\n"); eaddr[0]= 0x2a; memcpy(&eaddr[1], &ticks, sizeof(u_int32_t)); eaddr[5] = sc->sc_unit; } else { bzero(eaddr, sizeof(eaddr)); for (i = 0; i < (ETHER_ADDR_LEN * 2); i++) { u_int8_t c = eaddr_str[i]; if ((c >= '0') && (c <= '9')) { c -= '0'; } else { c -= 'A' - 10; } c &= 0xf; if ((i & 1) == 0) { c <<= 4; } eaddr[i / 2] |= c; } } ifp = if_alloc(IFT_ETHER); if (ifp == NULL) { device_printf(dev, "cannot if_alloc()\n"); goto detach; } ifp->if_softc = sc; if_initname(ifp, "cdce", sc->sc_unit); ifp->if_mtu = ETHERMTU; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = cdce_ioctl_cb; ifp->if_output = ether_output; ifp->if_start = cdce_start_cb; ifp->if_init = cdce_init_cb; ifp->if_baudrate = 11000000; ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; /* no IFM type for 11Mbps USB, so go with 10baseT */ ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_10_T, 0, 0); ifmedia_set(&sc->sc_ifmedia, IFM_ETHER | IFM_10_T); sc->sc_ifp = ifp; ether_ifattach(ifp, eaddr); return 0; /* success */ detach: cdce_detach(dev); return ENXIO; /* failure */ } static int cdce_detach(device_t dev) { struct cdce_softc * sc = device_get_softc(dev); struct ifnet * ifp; mtx_lock(&(sc->sc_mtx)); cdce_stop(sc); ifp = sc->sc_ifp; mtx_unlock(&(sc->sc_mtx)); /* get rid of any late children */ bus_generic_detach(dev); if (ifp) { ether_ifdetach(ifp); if_free(ifp); ifmedia_removeall(&(sc->sc_ifmedia)); } usbd_transfer_unsetup(sc->sc_xfer, CDCE_ENDPT_MAX); mtx_destroy(&(sc->sc_mtx)); return (0); } static void cdce_start_cb(struct ifnet *ifp) { struct cdce_softc *sc = ifp->if_softc; mtx_lock(&(sc->sc_mtx)); cdce_start_transfers(sc); mtx_unlock(&(sc->sc_mtx)); return; } static void cdce_start_transfers(struct cdce_softc *sc) { if ((sc->sc_flags & CDCE_FLAG_LL_READY) && (sc->sc_flags & CDCE_FLAG_HL_READY)) { /* start the USB transfers, * if not already started: */ usbd_transfer_start(sc->sc_xfer[1]); usbd_transfer_start(sc->sc_xfer[0]); } return; } static void cdce_bulk_write_clear_stall_callback(struct usbd_xfer *xfer) { struct cdce_softc *sc = xfer->priv_sc; struct usbd_xfer *xfer_other = sc->sc_xfer[0]; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, xfer_other); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, xfer_other); sc->sc_flags &= ~CDCE_FLAG_WRITE_STALL; usbd_transfer_start(xfer_other); return; tr_error: /* bomb out */ sc->sc_flags &= ~CDCE_FLAG_WRITE_STALL; DPRINTF(sc, 0, "bulk write pipe stopped\n"); return; } static void cdce_bulk_write_callback(struct usbd_xfer *xfer) { struct cdce_softc *sc = xfer->priv_sc; struct ifnet *ifp = sc->sc_ifp; struct mbuf *m; u_int32_t crc; USBD_CHECK_STATUS(xfer); tr_error: DPRINTF(sc, 10, "transfer error, %s\n", usbd_errstr(xfer->error)); if (xfer->error != USBD_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= CDCE_FLAG_WRITE_STALL; usbd_transfer_start(sc->sc_xfer[2]); } ifp->if_oerrors++; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; return; tr_transferred: DPRINTF(sc, 10, "transfer complete\n"); ifp->if_opackets++; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; tr_setup: if (sc->sc_flags & CDCE_FLAG_WRITE_STALL) { usbd_transfer_start(sc->sc_xfer[2]); goto done; } IF_DEQUEUE(&(ifp->if_snd), m); if (m == NULL) { goto done; } if (m->m_pkthdr.len > MCLBYTES) { m->m_pkthdr.len = MCLBYTES; } xfer->length = m->m_pkthdr.len; usbd_m_copy_in(&(xfer->buf_data), 0, m, 0, m->m_pkthdr.len); if (sc->sc_flags & CDCE_FLAG_ZAURUS) { /* Zaurus wants a 32-bit CRC appended to every frame */ crc = htole32(cdce_m_crc32(m, 0, m->m_pkthdr.len)); usbd_copy_in(&(xfer->buf_data), m->m_pkthdr.len, &crc, 4); xfer->length += 4; } /* * if there's a BPF listener, bounce a copy * of this frame to him: */ BPF_MTAP(ifp, m); m_freem(m); usbd_start_hardware(xfer); ifp->if_drv_flags |= IFF_DRV_OACTIVE; done: return; } static int32_t #ifdef __FreeBSD__ cdce_m_crc32_cb(void *arg, void *src, u_int32_t count) #else cdce_m_crc32_cb(void *arg, caddr_t src, u_int32_t count) #endif { register u_int32_t *p_crc = arg; *p_crc = crc32_raw(src, count, *p_crc); return 0; } static u_int32_t cdce_m_crc32(struct mbuf *m, u_int32_t src_offset, u_int32_t src_len) { register int error; u_int32_t crc = 0xFFFFFFFF; error = m_apply(m, src_offset, src_len, &cdce_m_crc32_cb, &crc); return (crc ^ 0xFFFFFFFF); } static void cdce_stop(struct cdce_softc *sc) { struct ifnet *ifp = sc->sc_ifp; /* immediate configuration */ if (ifp) { /* clear flags */ ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); } sc->sc_flags &= ~(CDCE_FLAG_HL_READY| CDCE_FLAG_LL_READY); /* stop all the transfers, * if not already stopped: */ if (sc->sc_xfer[0]) { usbd_transfer_stop(sc->sc_xfer[0]); } if (sc->sc_xfer[1]) { usbd_transfer_stop(sc->sc_xfer[1]); } if (sc->sc_xfer[2]) { usbd_transfer_stop(sc->sc_xfer[2]); } if (sc->sc_xfer[3]) { usbd_transfer_stop(sc->sc_xfer[3]); } return; } static int cdce_shutdown(device_t dev) { struct cdce_softc *sc = device_get_softc(dev); mtx_lock(&(sc->sc_mtx)); cdce_stop(sc); mtx_unlock(&(sc->sc_mtx)); return (0); } static int cdce_ioctl_cb(struct ifnet *ifp, u_long command, caddr_t data) { struct cdce_softc * sc = ifp->if_softc; int error = 0; mtx_lock(&(sc->sc_mtx)); switch(command) { case SIOCSIFFLAGS: if (ifp->if_flags & IFF_UP) { if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { cdce_init_cb(sc); } } else { if (ifp->if_drv_flags & IFF_DRV_RUNNING) { cdce_stop(sc); } } break; case SIOCSIFMEDIA: case SIOCGIFMEDIA: error = ifmedia_ioctl(ifp, (void *)data, &sc->sc_ifmedia, command); break; default: error = ether_ioctl(ifp, command, data); break; } mtx_unlock(&(sc->sc_mtx)); return (error); } static void cdce_init_cb(void *arg) { struct cdce_softc * sc = arg; struct ifnet * ifp; mtx_lock(&(sc->sc_mtx)); ifp = sc->sc_ifp; /* immediate configuration */ cdce_stop(sc); ifp->if_drv_flags |= IFF_DRV_RUNNING; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; sc->sc_flags |= (CDCE_FLAG_READ_STALL| CDCE_FLAG_WRITE_STALL| CDCE_FLAG_LL_READY| CDCE_FLAG_HL_READY); cdce_start_transfers(sc); mtx_unlock(&(sc->sc_mtx)); return; } static void cdce_bulk_read_clear_stall_callback(struct usbd_xfer *xfer) { struct cdce_softc *sc = xfer->priv_sc; struct usbd_xfer *xfer_other = sc->sc_xfer[1]; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, xfer_other); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, xfer_other); sc->sc_flags &= ~CDCE_FLAG_READ_STALL; usbd_transfer_start(xfer_other); return; tr_error: /* bomb out */ sc->sc_flags &= ~CDCE_FLAG_READ_STALL; DPRINTF(sc, 0, "bulk read pipe stopped\n"); return; } static void cdce_bulk_read_callback(struct usbd_xfer *xfer) { struct cdce_softc *sc = xfer->priv_sc; struct ifnet *ifp = sc->sc_ifp; struct mbuf *m; USBD_CHECK_STATUS(xfer); tr_error: if (xfer->error != USBD_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= CDCE_FLAG_READ_STALL; usbd_transfer_start(sc->sc_xfer[3]); } DPRINTF(sc, 0, "bulk read error, %s\n", usbd_errstr(xfer->error)); return; tr_transferred: if (sc->sc_flags & CDCE_FLAG_ZAURUS) { /* Strip off CRC added by Zaurus */ if (xfer->actlen >= 4) { xfer->actlen -= 4; } } if (xfer->actlen < sizeof(struct ether_header)) { ifp->if_ierrors++; goto tr_setup; } m = usbd_ether_get_mbuf(); if (m == NULL) { ifp->if_ierrors++; goto tr_setup; } xfer->actlen = min(xfer->actlen, m->m_len); usbd_copy_out(&(xfer->buf_data), 0, m->m_data, xfer->actlen); ifp->if_ipackets++; m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = m->m_len = xfer->actlen; (ifp->if_input)(ifp, m); tr_setup: if (sc->sc_flags & CDCE_FLAG_READ_STALL) { usbd_transfer_start(sc->sc_xfer[3]); } else { usbd_start_hardware(xfer); } return; } static int cdce_ifmedia_upd_cb(struct ifnet *ifp) { /* no-op, cdce has only 1 possible media type */ return 0; } static void cdce_ifmedia_sts_cb(struct ifnet * const ifp, struct ifmediareq *req) { req->ifm_status = IFM_AVALID | IFM_ACTIVE; req->ifm_active = IFM_ETHER | IFM_10_T; } pwcbsd/usb/if_cdcereg.h000644 000423 000000 00000005015 10551670753 015624 0ustar00luigiwheel000000 000000 /* * Copyright (c) 2003-2005 Craig Boston * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul, THE VOICES IN HIS HEAD OR * THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD: src/sys/dev/usb/if_cdcereg.h,v 1.6 2005/09/26 05:29:46 sobomax Exp $ */ #ifndef _USB_IF_CDCEREG_H_ #define _USB_IF_CDCEREG_H_ #define CDCE_ENDPT_MAX 4 struct cdce_type { struct usb_devno cdce_dev; uint16_t cdce_flags; }; struct cdce_softc { struct ifmedia sc_ifmedia; struct mtx sc_mtx; struct ifnet *sc_ifp; struct usbd_xfer *sc_xfer[CDCE_ENDPT_MAX]; struct usbd_device *sc_udev; device_t sc_dev; uint32_t sc_unit; uint16_t sc_flags; #define CDCE_FLAG_ZAURUS 0x0001 #define CDCE_FLAG_NO_UNION 0x0002 #define CDCE_FLAG_LL_READY 0x0004 #define CDCE_FLAG_HL_READY 0x0008 #define CDCE_FLAG_WRITE_STALL 0x0010 #define CDCE_FLAG_READ_STALL 0x0020 uint8_t sc_name[16]; uint8_t sc_data_iface_no; uint8_t sc_data_iface_index; }; #endif /* _USB_IF_CDCEREG_H_ */ pwcbsd/usb/if_cue.c000644 000423 000000 00000057136 10551670753 015012 0ustar00luigiwheel000000 000000 /*- * Copyright (c) 1997, 1998, 1999, 2000 * Bill Paul . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD: src/sys/dev/usb/if_cue.c,v 1.63 2006/09/07 00:06:41 imp Exp $"); /* * CATC USB-EL1210A USB to ethernet driver. Used in the CATC Netmate * adapters and others. * * Written by Bill Paul * Electrical Engineering Department * Columbia University, New York City */ /* * The CATC USB-EL1210A provides USB ethernet support at 10Mbps. The * RX filter uses a 512-bit multicast hash table, single perfect entry * for the station address, and promiscuous mode. Unlike the ADMtek * and KLSI chips, the CATC ASIC supports read and write combining * mode where multiple packets can be transfered using a single bulk * transaction, which helps performance a great deal. */ /* * NOTE: all function names beginning like "cue_cfg_" can only * be called from within the config thread function ! */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define usbd_config_td_cc cue_config_copy #define usbd_config_td_softc cue_softc #include #include #include #include #include "usbdevs.h" #include /* * Various supported device vendors/products. */ static struct cue_type cue_devs[] = { { USB_VENDOR_CATC, USB_PRODUCT_CATC_NETMATE }, { USB_VENDOR_CATC, USB_PRODUCT_CATC_NETMATE2 }, { USB_VENDOR_SMARTBRIDGES, USB_PRODUCT_SMARTBRIDGES_SMARTLINK }, { 0, 0 } }; /* prototypes */ static device_probe_t cue_probe; static device_attach_t cue_attach; static device_detach_t cue_detach; static device_shutdown_t cue_shutdown; static void cue_cfg_do_request(struct cue_softc *sc, usb_device_request_t *req, void *data); static u_int8_t cue_cfg_csr_read_1(struct cue_softc *sc, u_int16_t reg); static u_int16_t cue_cfg_csr_read_2(struct cue_softc *sc, u_int8_t reg); static void cue_cfg_csr_write_1(struct cue_softc *sc, u_int16_t reg, u_int16_t val); static void cue_cfg_mem(struct cue_softc *sc, u_int8_t cmd, u_int16_t addr, void *buf, u_int16_t len); static void cue_cfg_getmac(struct cue_softc *sc, void *buf); static uint32_t cue_mchash(const uint8_t *addr); static void cue_cfg_promisc_upd(struct cue_softc *sc, struct cue_config_copy *cc, u_int16_t refcount); static void cue_config_copy(struct cue_softc *sc, struct cue_config_copy *cc, u_int16_t refcount); static void cue_cfg_reset(struct cue_softc *sc); static void cue_cfg_first_time_setup(struct cue_softc *sc, struct cue_config_copy *cc, u_int16_t refcount); static void cue_bulk_read_clear_stall_callback(struct usbd_xfer *xfer); static void cue_bulk_read_callback(struct usbd_xfer *xfer); static void cue_cfg_tick(struct cue_softc *sc, struct cue_config_copy *cc, u_int16_t refcount); static void cue_start_cb(struct ifnet *ifp); static void cue_start_transfers(struct cue_softc *sc); static void cue_bulk_write_clear_stall_callback(struct usbd_xfer *xfer); static void cue_bulk_write_callback(struct usbd_xfer *xfer); static void cue_init_cb(void *arg); static void cue_cfg_init(struct cue_softc *sc, struct cue_config_copy *cc, u_int16_t refcount); static int cue_ioctl_cb(struct ifnet *ifp, u_long command, caddr_t data); static void cue_watchdog(void *arg); static void cue_cfg_stop(struct cue_softc *sc, struct cue_config_copy *cc, u_int16_t refcount); #define DPRINTF(...) static const struct usbd_config cue_config[CUE_ENDPT_MAX] = { [0] = { .type = UE_BULK, .endpoint = -1, /* any */ .direction = UE_DIR_OUT, .bufsize = (MCLBYTES + 2), .flags = (USBD_USE_DMA), .callback = &cue_bulk_write_callback, .timeout = 10000, /* 10 seconds */ }, [1] = { .type = UE_BULK, .endpoint = -1, /* any */ .direction = UE_DIR_IN, .bufsize = (MCLBYTES + 2), .flags = (USBD_USE_DMA|USBD_SHORT_XFER_OK), .callback = &cue_bulk_read_callback, }, [2] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .flags = USBD_USE_DMA, .callback = &cue_bulk_write_clear_stall_callback, .timeout = 1000, /* 1 second */ }, [3] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .flags = USBD_USE_DMA, .callback = &cue_bulk_read_clear_stall_callback, .timeout = 1000, /* 1 second */ }, }; static device_method_t cue_methods[] = { /* Device interface */ DEVMETHOD(device_probe, cue_probe), DEVMETHOD(device_attach, cue_attach), DEVMETHOD(device_detach, cue_detach), DEVMETHOD(device_shutdown, cue_shutdown), { 0, 0 } }; static driver_t cue_driver = { .name = "cue", .methods = cue_methods, .size = sizeof(struct cue_softc), }; static devclass_t cue_devclass; DRIVER_MODULE(cue, uhub, cue_driver, cue_devclass, usbd_driver_load, 0); MODULE_DEPEND(cue, usb, 1, 1, 1); MODULE_DEPEND(cue, ether, 1, 1, 1); static void cue_cfg_do_request(struct cue_softc *sc, usb_device_request_t *req, void *data) { u_int16_t length; usbd_status err; if (usbd_config_td_is_gone(&(sc->sc_config_td))) { goto error; } err = usbd_do_request_flags_mtx(sc->sc_udev, &(sc->sc_mtx), req, data, 0, NULL, 1000); if (err) { DPRINTF(sc, 0, "device request failed, err=%s " "(ignored)\n", usbd_errstr(err)); error: length = UGETW(req->wLength); if ((req->bmRequestType & UT_READ) && length) { bzero(data, length); } } return; } #define CUE_CFG_SETBIT(sc, reg, x) \ cue_cfg_csr_write_1(sc, reg, cue_cfg_csr_read_1(sc, reg) | (x)) #define CUE_CFG_CLRBIT(sc, reg, x) \ cue_cfg_csr_write_1(sc, reg, cue_cfg_csr_read_1(sc, reg) & ~(x)) static u_int8_t cue_cfg_csr_read_1(struct cue_softc *sc, u_int16_t reg) { usb_device_request_t req; u_int8_t val; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = CUE_CMD_READREG; USETW(req.wValue, 0); USETW(req.wIndex, reg); USETW(req.wLength, 1); cue_cfg_do_request(sc, &req, &val); return val; } static u_int16_t cue_cfg_csr_read_2(struct cue_softc *sc, u_int8_t reg) { usb_device_request_t req; u_int16_t val; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = CUE_CMD_READREG; USETW(req.wValue, 0); USETW(req.wIndex, reg); USETW(req.wLength, 2); cue_cfg_do_request(sc, &req, &val); return le16toh(val); } static void cue_cfg_csr_write_1(struct cue_softc *sc, u_int16_t reg, u_int16_t val) { usb_device_request_t req; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = CUE_CMD_WRITEREG; USETW(req.wValue, val); USETW(req.wIndex, reg); USETW(req.wLength, 0); cue_cfg_do_request(sc, &req, NULL); return; } static void cue_cfg_mem(struct cue_softc *sc, u_int8_t cmd, u_int16_t addr, void *buf, u_int16_t len) { usb_device_request_t req; if (cmd == CUE_CMD_READSRAM) { req.bmRequestType = UT_READ_VENDOR_DEVICE; } else { req.bmRequestType = UT_WRITE_VENDOR_DEVICE; } req.bRequest = cmd; USETW(req.wValue, 0); USETW(req.wIndex, addr); USETW(req.wLength, len); cue_cfg_do_request(sc, &req, buf); return; } static void cue_cfg_getmac(struct cue_softc *sc, void *buf) { usb_device_request_t req; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = CUE_CMD_GET_MACADDR; USETW(req.wValue, 0); USETW(req.wIndex, 0); USETW(req.wLength, ETHER_ADDR_LEN); cue_cfg_do_request(sc, &req, buf); return; } #define CUE_BITS 9 static uint32_t cue_mchash(const uint8_t *addr) { uint32_t crc; /* Compute CRC for the address value. */ crc = ether_crc32_le(addr, ETHER_ADDR_LEN); return (crc & ((1 << CUE_BITS) - 1)); } static void cue_cfg_promisc_upd(struct cue_softc *sc, struct cue_config_copy *cc, u_int16_t refcount) { if (cc == NULL) { /* nothing to do */ return; } /* if we want promiscuous mode, set the allframes bit */ if (cc->if_flags & IFF_PROMISC) { CUE_CFG_SETBIT(sc, CUE_ETHCTL, CUE_ETHCTL_PROMISC); } else { CUE_CFG_CLRBIT(sc, CUE_ETHCTL, CUE_ETHCTL_PROMISC); } /* write multicast hash-bits */ cue_cfg_mem(sc, CUE_CMD_WRITESRAM, CUE_MCAST_TABLE_ADDR, cc->if_hash, CUE_MCAST_TABLE_LEN); return; } static void cue_config_copy(struct cue_softc *sc, struct cue_config_copy *cc, u_int16_t refcount) { struct ifnet * ifp = sc->sc_ifp; struct ifmultiaddr * ifma; u_int16_t h; u_int16_t i; bzero(cc, sizeof(*cc)); if (ifp) { for (i = 0; i < ETHER_ADDR_LEN; i++) { cc->if_lladdr[i] = IF_LLADDR(ifp)[i]; } cc->if_flags = ifp->if_flags; if ((ifp->if_flags & IFF_ALLMULTI) || (ifp->if_flags & IFF_PROMISC)) { for (i = 0; i < CUE_MCAST_TABLE_LEN; i++) { cc->if_hash[i] = 0xFF; } } else { /* program new hash bits */ IF_ADDR_LOCK(ifp); TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) { continue; } h = cue_mchash(LLADDR((struct sockaddr_dl *)(ifma->ifma_addr))); cc->if_hash[h >> 3] |= 1 << (h & 0x7); } IF_ADDR_UNLOCK(ifp); /* * Also include the broadcast address in the filter * so we can receive broadcast frames. */ if (ifp->if_flags & IFF_BROADCAST) { h = cue_mchash(ifp->if_broadcastaddr); cc->if_hash[h >> 3] |= 1 << (h & 0x7); } } } return; } static void cue_cfg_reset(struct cue_softc *sc) { usb_device_request_t req; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = CUE_CMD_RESET; USETW(req.wValue, 0); USETW(req.wIndex, 0); USETW(req.wLength, 0); cue_cfg_do_request(sc, &req, NULL); /* wait a little while for the chip * to get its brains in order: */ (void) usbd_config_td_sleep(&(sc->sc_config_td), hz/100); return; } static int cue_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct cue_type *t; if (uaa->iface != NULL) { return(UMATCH_NONE); } t = cue_devs; while(t->cue_vid) { if ((uaa->vendor == t->cue_vid) && (uaa->product == t->cue_did)) { return UMATCH_VENDOR_PRODUCT; } t++; } return UMATCH_NONE; } static int cue_attach(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct cue_softc *sc = device_get_softc(dev); int32_t error; if (sc == NULL) { return ENOMEM; } sc->sc_udev = uaa->device; sc->sc_dev = dev; sc->sc_unit = device_get_unit(dev); usbd_set_desc(dev, uaa->device); mtx_init(&(sc->sc_mtx), "cue lock", NULL, MTX_DEF | MTX_RECURSE); __callout_init_mtx(&(sc->sc_watchdog), &(sc->sc_mtx), CALLOUT_RETURNUNLOCKED); error = usbd_set_config_no(uaa->device, CUE_CONFIG_NO, 0); if (error) { device_printf(dev, "setting config " "number failed!\n"); goto detach; } error = usbd_transfer_setup(uaa->device, CUE_IFACE_IDX, sc->sc_xfer, cue_config, CUE_ENDPT_MAX, sc, &(sc->sc_mtx)); if (error) { device_printf(dev, "allocating USB " "transfers failed!\n"); goto detach; } error = usbd_config_td_setup(&(sc->sc_config_td), sc, &(sc->sc_mtx), &cue_config_copy, NULL, sizeof(struct cue_config_copy), 16); if (error) { device_printf(dev, "could not setup config " "thread!\n"); goto detach; } mtx_lock(&(sc->sc_mtx)); /* start setup */ usbd_config_td_queue_command (&(sc->sc_config_td), &cue_cfg_first_time_setup, 0); /* start watchdog (will exit mutex) */ cue_watchdog(sc); return 0; /* success */ detach: cue_detach(dev); return ENXIO; /* failure */ } static void cue_cfg_first_time_setup(struct cue_softc *sc, struct cue_config_copy *cc, u_int16_t refcount) { u_int8_t eaddr[ETHER_ADDR_LEN]; struct ifnet * ifp; if (cc == NULL) { return; } #if 0 /* Reset the adapter. */ cue_cfg_reset(sc); #endif /* * Get station address. */ cue_cfg_getmac(sc, eaddr); mtx_unlock(&(sc->sc_mtx)); ifp = if_alloc(IFT_ETHER); mtx_lock(&(sc->sc_mtx)); if (ifp == NULL) { printf("cue%d: could not if_alloc()\n", sc->sc_unit); goto done; } ifp->if_softc = sc; if_initname(ifp, "cue", sc->sc_unit); ifp->if_mtu = ETHERMTU; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = cue_ioctl_cb; ifp->if_start = cue_start_cb; ifp->if_watchdog = NULL; ifp->if_init = cue_init_cb; ifp->if_baudrate = 10000000; ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; sc->sc_ifp = ifp; ether_ifattach(ifp, eaddr); done: return; } static int cue_detach(device_t dev) { struct cue_softc * sc = device_get_softc(dev); struct ifnet * ifp; mtx_lock(&(sc->sc_mtx)); usbd_config_td_stop(&(sc->sc_config_td)); __callout_stop(&(sc->sc_watchdog)); cue_cfg_stop(sc, NULL, 0); ifp = sc->sc_ifp; mtx_unlock(&(sc->sc_mtx)); /* get rid of any late children */ bus_generic_detach(dev); if (ifp) { ether_ifdetach(ifp); if_free(ifp); } usbd_transfer_unsetup(sc->sc_xfer, CUE_ENDPT_MAX); usbd_config_td_unsetup(&(sc->sc_config_td)); __callout_drain(&(sc->sc_watchdog)); mtx_destroy(&(sc->sc_mtx)); return 0; } static void cue_bulk_read_clear_stall_callback(struct usbd_xfer *xfer) { struct cue_softc *sc = xfer->priv_sc; struct usbd_xfer *xfer_other = sc->sc_xfer[1]; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, xfer_other); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, xfer_other); sc->sc_flags &= ~CUE_FLAG_READ_STALL; usbd_transfer_start(xfer_other); return; tr_error: /* bomb out */ sc->sc_flags &= ~CUE_FLAG_READ_STALL; DPRINTF(sc, 0, "bulk read pipe stopped\n"); return; } static void cue_bulk_read_callback(struct usbd_xfer *xfer) { struct cue_softc *sc = xfer->priv_sc; struct ifnet *ifp = sc->sc_ifp; struct mbuf *m; u_int8_t buf[2]; u_int16_t len; USBD_CHECK_STATUS(xfer); tr_error: if (xfer->error != USBD_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= CUE_FLAG_READ_STALL; usbd_transfer_start(sc->sc_xfer[3]); } DPRINTF(sc, 0, "bulk read error, %s\n", usbd_errstr(xfer->error)); return; tr_transferred: if (xfer->actlen <= (2 + sizeof(struct ether_header))) { ifp->if_ierrors++; goto tr_setup; } usbd_copy_out(&(xfer->buf_data), 0, buf, 2); len = buf[0] | (buf[1] << 8); xfer->actlen -= 2; m = usbd_ether_get_mbuf(); if (m == NULL) { ifp->if_ierrors++; goto tr_setup; } xfer->actlen = min(xfer->actlen, m->m_len); xfer->actlen = min(xfer->actlen, len); usbd_copy_out(&(xfer->buf_data), 2, m->m_data, xfer->actlen); ifp->if_ipackets++; m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = m->m_len = xfer->actlen; (ifp->if_input)(ifp, m); tr_setup: if (sc->sc_flags & CUE_FLAG_READ_STALL) { usbd_transfer_start(sc->sc_xfer[3]); } else { usbd_start_hardware(xfer); } return; } static void cue_cfg_tick(struct cue_softc *sc, struct cue_config_copy *cc, u_int16_t refcount) { struct ifnet * ifp = sc->sc_ifp; if ((cc == NULL) || (ifp == NULL)) { /* not ready */ return; } ifp->if_collisions += cue_cfg_csr_read_2(sc, CUE_TX_SINGLECOLL); ifp->if_collisions += cue_cfg_csr_read_2(sc, CUE_TX_MULTICOLL); ifp->if_collisions += cue_cfg_csr_read_2(sc, CUE_TX_EXCESSCOLL); if (cue_cfg_csr_read_2(sc, CUE_RX_FRAMEERR)) { ifp->if_ierrors++; } /* start stopped transfers, if any */ cue_start_transfers(sc); return; } static void cue_start_cb(struct ifnet *ifp) { struct cue_softc * sc = ifp->if_softc; mtx_lock(&(sc->sc_mtx)); cue_start_transfers(sc); mtx_unlock(&(sc->sc_mtx)); return; } static void cue_start_transfers(struct cue_softc *sc) { if ((sc->sc_flags & CUE_FLAG_LL_READY) && (sc->sc_flags & CUE_FLAG_HL_READY)) { /* start the USB transfers, * if not already started: */ usbd_transfer_start(sc->sc_xfer[1]); usbd_transfer_start(sc->sc_xfer[0]); } return; } static void cue_bulk_write_clear_stall_callback(struct usbd_xfer *xfer) { struct cue_softc *sc = xfer->priv_sc; struct usbd_xfer *xfer_other = sc->sc_xfer[0]; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, xfer_other); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, xfer_other); sc->sc_flags &= ~CUE_FLAG_WRITE_STALL; usbd_transfer_start(xfer_other); return; tr_error: /* bomb out */ sc->sc_flags &= ~CUE_FLAG_WRITE_STALL; DPRINTF(sc, 0, "bulk write pipe stopped\n"); return; } static void cue_bulk_write_callback(struct usbd_xfer *xfer) { struct cue_softc *sc = xfer->priv_sc; struct ifnet *ifp = sc->sc_ifp; struct mbuf *m; u_int8_t buf[2]; USBD_CHECK_STATUS(xfer); tr_error: DPRINTF(sc, 10, "transfer error, %s\n", usbd_errstr(xfer->error)); if (xfer->error != USBD_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= CUE_FLAG_WRITE_STALL; usbd_transfer_start(sc->sc_xfer[2]); } ifp->if_oerrors++; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; return; tr_transferred: DPRINTF(sc, 10, "transfer complete\n"); ifp->if_opackets++; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; tr_setup: if (sc->sc_flags & CUE_FLAG_WRITE_STALL) { usbd_transfer_start(sc->sc_xfer[2]); goto done; } IF_DEQUEUE(&(ifp->if_snd), m); if (m == NULL) { goto done; } if (m->m_pkthdr.len > MCLBYTES) { m->m_pkthdr.len = MCLBYTES; } xfer->length = (m->m_pkthdr.len + 2); /* the first two bytes are the frame length */ buf[0] = (u_int8_t)(m->m_pkthdr.len); buf[1] = (u_int8_t)(m->m_pkthdr.len >> 8); usbd_copy_in(&(xfer->buf_data), 0, buf, 2); usbd_m_copy_in(&(xfer->buf_data), 2, m, 0, m->m_pkthdr.len); /* * If there's a BPF listener, bounce a copy of this frame * to him. */ BPF_MTAP(ifp, m); m_freem(m); usbd_start_hardware(xfer); ifp->if_drv_flags |= IFF_DRV_OACTIVE; done: return; } static void cue_init_cb(void *arg) { struct cue_softc *sc = arg; mtx_lock(&(sc->sc_mtx)); usbd_config_td_queue_command (&(sc->sc_config_td), &cue_cfg_init, 0); mtx_unlock(&(sc->sc_mtx)); return; } static void cue_cfg_init(struct cue_softc *sc, struct cue_config_copy *cc, u_int16_t refcount) { u_int8_t i; if (cc == NULL) { /* immediate configuration */ struct ifnet *ifp = sc->sc_ifp; cue_cfg_stop(sc, NULL, 0); ifp->if_drv_flags |= IFF_DRV_RUNNING; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; sc->sc_flags |= CUE_FLAG_HL_READY; return; } /* * Cancel pending I/O and free all RX/TX buffers. */ cue_cfg_stop(sc, cc, 0); #if 0 cue_cfg_reset(sc); #endif /* Set MAC address */ for (i = 0; i < ETHER_ADDR_LEN; i++) { cue_cfg_csr_write_1(sc, CUE_PAR0 - i, cc->if_lladdr[i]); } /* Enable RX logic. */ cue_cfg_csr_write_1(sc, CUE_ETHCTL, CUE_ETHCTL_RX_ON|CUE_ETHCTL_MCAST_ON); /* Load the multicast filter */ cue_cfg_promisc_upd(sc, cc, 0); /* * Set the number of RX and TX buffers that we want * to reserve inside the ASIC. */ cue_cfg_csr_write_1(sc, CUE_RX_BUFPKTS, CUE_RX_FRAMES); cue_cfg_csr_write_1(sc, CUE_TX_BUFPKTS, CUE_TX_FRAMES); /* Set advanced operation modes. */ cue_cfg_csr_write_1(sc, CUE_ADVANCED_OPMODES, CUE_AOP_EMBED_RXLEN|0x01); /* 1 wait state */ /* Program the LED operation. */ cue_cfg_csr_write_1(sc, CUE_LEDCTL, CUE_LEDCTL_FOLLOW_LINK); sc->sc_flags |= (CUE_FLAG_READ_STALL| CUE_FLAG_WRITE_STALL| CUE_FLAG_LL_READY); cue_start_transfers(sc); return; } static int cue_ioctl_cb(struct ifnet *ifp, u_long command, caddr_t data) { struct cue_softc * sc = ifp->if_softc; int error = 0; mtx_lock(&(sc->sc_mtx)); switch(command) { case SIOCSIFFLAGS: if (ifp->if_flags & IFF_UP) { if (ifp->if_drv_flags & IFF_DRV_RUNNING) { usbd_config_td_queue_command (&(sc->sc_config_td), &cue_cfg_promisc_upd, 0); } else { usbd_config_td_queue_command (&(sc->sc_config_td), &cue_cfg_init, 0); } } else { if (ifp->if_drv_flags & IFF_DRV_RUNNING) { usbd_config_td_queue_command (&(sc->sc_config_td), &cue_cfg_stop, 0); } } break; case SIOCADDMULTI: case SIOCDELMULTI: usbd_config_td_queue_command (&(sc->sc_config_td), &cue_cfg_promisc_upd, 0); break; default: error = ether_ioctl(ifp, command, data); break; } mtx_unlock(&(sc->sc_mtx)); return(error); } static void cue_watchdog(void *arg) { struct cue_softc * sc = arg; mtx_assert(&(sc->sc_mtx), MA_OWNED); usbd_config_td_queue_command (&(sc->sc_config_td), &cue_cfg_tick, 0); __callout_reset(&(sc->sc_watchdog), hz, &cue_watchdog, sc); mtx_unlock(&(sc->sc_mtx)); return; } /* * Stop the adapter and free any mbufs allocated to the * RX and TX lists. */ static void cue_cfg_stop(struct cue_softc *sc, struct cue_config_copy *cc, u_int16_t refcount) { if (cc == NULL) { /* immediate configuration */ struct ifnet *ifp = sc->sc_ifp; if (ifp) { /* clear flags */ ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); } sc->sc_flags &= ~(CUE_FLAG_HL_READY| CUE_FLAG_LL_READY); /* stop all the transfers, * if not already stopped: */ if (sc->sc_xfer[0]) { usbd_transfer_stop(sc->sc_xfer[0]); } if (sc->sc_xfer[1]) { usbd_transfer_stop(sc->sc_xfer[1]); } if (sc->sc_xfer[2]) { usbd_transfer_stop(sc->sc_xfer[2]); } if (sc->sc_xfer[3]) { usbd_transfer_stop(sc->sc_xfer[3]); } return; } cue_cfg_csr_write_1(sc, CUE_ETHCTL, 0); cue_cfg_reset(sc); return; } /* * Stop all chip I/O so that the kernel's probe routines don't * get confused by errant DMAs when rebooting. */ static int cue_shutdown(device_t dev) { struct cue_softc *sc = device_get_softc(dev); mtx_lock(&(sc->sc_mtx)); usbd_config_td_queue_command (&(sc->sc_config_td), &cue_cfg_stop, 0); mtx_unlock(&(sc->sc_mtx)); return 0; } pwcbsd/usb/if_cuereg.h000644 000423 000000 00000012004 10551670753 015476 0ustar00luigiwheel000000 000000 /*- * Copyright (c) 1997, 1998, 1999, 2000 * Bill Paul . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD: src/sys/dev/usb/if_cuereg.h,v 1.18 2005/06/10 16:49:15 brooks Exp $ */ /* * Definitions for the CATC Netmate II USB to ethernet controller. */ /* Vendor specific control commands. */ #define CUE_CMD_RESET 0xF4 #define CUE_CMD_GET_MACADDR 0xF2 #define CUE_CMD_WRITEREG 0xFA #define CUE_CMD_READREG 0xFB #define CUE_CMD_READSRAM 0xF1 #define CUE_CMD_WRITESRAM 0xFC /* Internal registers. */ #define CUE_TX_BUFCNT 0x20 #define CUE_RX_BUFCNT 0x21 #define CUE_ADVANCED_OPMODES 0x22 #define CUE_TX_BUFPKTS 0x23 #define CUE_RX_BUFPKTS 0x24 #define CUE_RX_MAXCHAIN 0x25 #define CUE_ETHCTL 0x60 #define CUE_ETHSTS 0x61 #define CUE_PAR5 0x62 #define CUE_PAR4 0x63 #define CUE_PAR3 0x64 #define CUE_PAR2 0x65 #define CUE_PAR1 0x66 #define CUE_PAR0 0x67 /* Error counters, all 16 bits wide. */ #define CUE_TX_SINGLECOLL 0x69 #define CUE_TX_MULTICOLL 0x6B #define CUE_TX_EXCESSCOLL 0x6D #define CUE_RX_FRAMEERR 0x6F #define CUE_LEDCTL 0x81 /* Advenced operating mode register. */ #define CUE_AOP_SRAMWAITS 0x03 #define CUE_AOP_EMBED_RXLEN 0x08 #define CUE_AOP_RXCOMBINE 0x10 #define CUE_AOP_TXCOMBINE 0x20 #define CUE_AOP_EVEN_PKT_READS 0x40 #define CUE_AOP_LOOPBK 0x80 /* Ethernet control register. */ #define CUE_ETHCTL_RX_ON 0x01 #define CUE_ETHCTL_LINK_POLARITY 0x02 #define CUE_ETHCTL_LINK_FORCE_OK 0x04 #define CUE_ETHCTL_MCAST_ON 0x08 #define CUE_ETHCTL_PROMISC 0x10 /* Ethernet status register. */ #define CUE_ETHSTS_NO_CARRIER 0x01 #define CUE_ETHSTS_LATECOLL 0x02 #define CUE_ETHSTS_EXCESSCOLL 0x04 #define CUE_ETHSTS_TXBUF_AVAIL 0x08 #define CUE_ETHSTS_BAD_POLARITY 0x10 #define CUE_ETHSTS_LINK_OK 0x20 /* LED control register. */ #define CUE_LEDCTL_BLINK_1X 0x00 #define CUE_LEDCTL_BLINK_2X 0x01 #define CUE_LEDCTL_BLINK_QUARTER_ON 0x02 #define CUE_LEDCTL_BLINK_QUARTER_OFF 0x03 #define CUE_LEDCTL_OFF 0x04 #define CUE_LEDCTL_FOLLOW_LINK 0x08 /* * Address in ASIC's internal SRAM where the multicast hash table lives. * The table is 64 bytes long, giving us a 512-bit table. We have to set * the bit that corresponds to the broadcast address in order to enable * reception of broadcast frames. */ #define CUE_MCAST_TABLE_ADDR 0xFA80 #define CUE_MCAST_TABLE_LEN 64 #define CUE_TIMEOUT 1000 #define CUE_MIN_FRAMELEN 60 #define CUE_RX_FRAMES 1 #define CUE_TX_FRAMES 1 #define CUE_CTL_READ 0x01 #define CUE_CTL_WRITE 0x02 #define CUE_CONFIG_NO 1 #define CUE_IFACE_IDX 0 /* The interrupt endpoint is currently unused by the KLSI part. */ #define CUE_ENDPT_MAX 4 struct cue_type { uint16_t cue_vid; uint16_t cue_did; }; struct cue_softc { struct usbd_config_td sc_config_td; struct __callout sc_watchdog; struct mtx sc_mtx; struct ifnet *sc_ifp; device_t sc_dev; struct usbd_device *sc_udev; struct usbd_xfer *sc_xfer[CUE_ENDPT_MAX]; uint32_t sc_unit; uint16_t sc_flags; #define CUE_FLAG_READ_STALL 0x0010 /* wait for clearing of stall */ #define CUE_FLAG_WRITE_STALL 0x0020 /* wait for clearing of stall */ #define CUE_FLAG_LL_READY 0x0040 /* Lower Layer Ready */ #define CUE_FLAG_HL_READY 0x0080 /* Higher Layer Ready */ #define CUE_FLAG_INTR_STALL 0x0100 /* wait for clearing of stall */ }; struct cue_config_copy { uint32_t if_flags; uint8_t if_lladdr[ETHER_ADDR_LEN]; uint8_t if_hash[CUE_MCAST_TABLE_LEN]; }; pwcbsd/usb/if_kue.c000644 000423 000000 00000062442 10551670753 015016 0ustar00luigiwheel000000 000000 /*- * Copyright (c) 1997, 1998, 1999, 2000 * Bill Paul . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD: src/sys/dev/usb/if_kue.c,v 1.70 2006/09/07 00:06:41 imp Exp $"); /* * Kawasaki LSI KL5KUSB101B USB to ethernet adapter driver. * * Written by Bill Paul * Electrical Engineering Department * Columbia University, New York City */ /* * The KLSI USB to ethernet adapter chip contains an USB serial interface, * ethernet MAC and embedded microcontroller (called the QT Engine). * The chip must have firmware loaded into it before it will operate. * Packets are passed between the chip and host via bulk transfers. * There is an interrupt endpoint mentioned in the software spec, however * it's currently unused. This device is 10Mbps half-duplex only, hence * there is no media selection logic. The MAC supports a 128 entry * multicast filter, though the exact size of the filter can depend * on the firmware. Curiously, while the software spec describes various * ethernet statistics counters, my sample adapter and firmware combination * claims not to support any statistics counters at all. * * Note that once we load the firmware in the device, we have to be * careful not to load it again: if you restart your computer but * leave the adapter attached to the USB controller, it may remain * powered on and retain its firmware. In this case, we don't need * to load the firmware a second time. * * Special thanks to Rob Furr for providing an ADS Technologies * adapter for development and testing. No monkeys were harmed during * the development of this driver. */ /* * NOTE: all function names beginning like "kue_cfg_" can only * be called from within the config thread function ! */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define usbd_config_td_cc kue_config_copy #define usbd_config_td_softc kue_softc #include #include #include #include #include "usbdevs.h" #include #include /* * Various supported device vendors/products. */ static struct kue_type kue_devs[] = { { USB_VENDOR_AOX, USB_PRODUCT_AOX_USB101 }, { USB_VENDOR_KLSI, USB_PRODUCT_AOX_USB101 }, { USB_VENDOR_ADS, USB_PRODUCT_ADS_UBS10BT }, { USB_VENDOR_ATEN, USB_PRODUCT_ATEN_UC10T }, { USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_EA101 }, { USB_VENDOR_PERACOM, USB_PRODUCT_PERACOM_ENET }, { USB_VENDOR_PERACOM, USB_PRODUCT_PERACOM_ENET2 }, { USB_VENDOR_ENTREGA, USB_PRODUCT_ENTREGA_E45 }, { USB_VENDOR_3COM, USB_PRODUCT_3COM_3C19250 }, { USB_VENDOR_COREGA, USB_PRODUCT_COREGA_ETHER_USB_T }, { USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650C }, { USB_VENDOR_SMC, USB_PRODUCT_SMC_2102USB }, { USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10T }, { USB_VENDOR_KLSI, USB_PRODUCT_KLSI_DUH3E10BT }, { USB_VENDOR_KLSI, USB_PRODUCT_KLSI_DUH3E10BTN }, { USB_VENDOR_PERACOM, USB_PRODUCT_PERACOM_ENET3 }, { USB_VENDOR_IODATA, USB_PRODUCT_IODATA_USBETT }, { USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_URE450 }, { 0, 0 } }; static device_probe_t kue_probe; static device_attach_t kue_attach; static device_detach_t kue_detach; static device_shutdown_t kue_shutdown; static void kue_cfg_do_request(struct kue_softc *sc, usb_device_request_t *req, void *data); static void kue_cfg_setword(struct kue_softc *sc, u_int8_t breq, u_int16_t word); static void kue_cfg_ctl(struct kue_softc *sc, u_int8_t rw, u_int8_t breq, u_int16_t val, void *data, u_int16_t len); static void kue_cfg_load_fw(struct kue_softc *sc); static void kue_cfg_promisc_upd(struct kue_softc *sc, struct kue_config_copy *cc, u_int16_t refcount); static void kue_config_copy(struct kue_softc *sc, struct kue_config_copy *cc, u_int16_t refcount); static void kue_cfg_reset(struct kue_softc *sc); static void kue_cfg_first_time_setup(struct kue_softc *sc, struct kue_config_copy *cc, u_int16_t refcount); static void kue_bulk_read_clear_stall_callback(struct usbd_xfer *xfer); static void kue_bulk_read_callback(struct usbd_xfer *xfer); static void kue_bulk_write_clear_stall_callback(struct usbd_xfer *xfer); static void kue_bulk_write_callback(struct usbd_xfer *xfer); static void kue_start_cb(struct ifnet *ifp); static void kue_start_transfers(struct kue_softc *sc); static void kue_init_cb(void *arg); static void kue_cfg_init(struct kue_softc *sc, struct kue_config_copy *cc, u_int16_t refcount); static int kue_ioctl_cb(struct ifnet *ifp, u_long command, caddr_t data); static void kue_cfg_tick(struct kue_softc *sc, struct kue_config_copy *cc, u_int16_t refcount); static void kue_watchdog(void *arg); static void kue_cfg_stop(struct kue_softc *sc, struct kue_config_copy *cc, u_int16_t refcount); #define DPRINTF(...) static const struct usbd_config kue_config[KUE_ENDPT_MAX] = { [0] = { .type = UE_BULK, .endpoint = -1, /* any */ .direction = UE_DIR_OUT, .bufsize = (MCLBYTES + 2 + 64), .flags = (USBD_USE_DMA), .callback = &kue_bulk_write_callback, .timeout = 10000, /* 10 seconds */ }, [1] = { .type = UE_BULK, .endpoint = -1, /* any */ .direction = UE_DIR_IN, .bufsize = (MCLBYTES + 2), .flags = (USBD_USE_DMA|USBD_SHORT_XFER_OK), .callback = &kue_bulk_read_callback, .timeout = 0, /* no timeout */ }, [2] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .flags = USBD_USE_DMA, .callback = &kue_bulk_write_clear_stall_callback, .timeout = 1000, /* 1 second */ }, [3] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .flags = USBD_USE_DMA, .callback = &kue_bulk_read_clear_stall_callback, .timeout = 1000, /* 1 second */ }, }; static device_method_t kue_methods[] = { /* Device interface */ DEVMETHOD(device_probe, kue_probe), DEVMETHOD(device_attach, kue_attach), DEVMETHOD(device_detach, kue_detach), DEVMETHOD(device_shutdown, kue_shutdown), { 0, 0 } }; static driver_t kue_driver = { .name = "kue", .methods = kue_methods, .size = sizeof(struct kue_softc), }; static devclass_t kue_devclass; DRIVER_MODULE(kue, uhub, kue_driver, kue_devclass, usbd_driver_load, 0); MODULE_DEPEND(kue, usb, 1, 1, 1); MODULE_DEPEND(kue, ether, 1, 1, 1); /* * We have a custom do_request function which is almost like the * regular do_request function, except it has a much longer timeout. * Why? Because we need to make requests over the control endpoint * to download the firmware to the device, which can take longer * than the default timeout. */ static void kue_cfg_do_request(struct kue_softc *sc, usb_device_request_t *req, void *data) { u_int16_t length; usbd_status err; if (usbd_config_td_is_gone(&(sc->sc_config_td))) { goto error; } err = usbd_do_request_flags_mtx(sc->sc_udev, &(sc->sc_mtx), req, data, 0, NULL, 60000); if (err) { DPRINTF(sc, 0, "device request failed, err=%s " "(ignored)\n", usbd_errstr(err)); error: length = UGETW(req->wLength); if ((req->bmRequestType & UT_READ) && length) { bzero(data, length); } } return; } static void kue_cfg_setword(struct kue_softc *sc, u_int8_t breq, u_int16_t word) { usb_device_request_t req; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = breq; USETW(req.wValue, word); USETW(req.wIndex, 0); USETW(req.wLength, 0); kue_cfg_do_request(sc, &req, NULL); return; } static void kue_cfg_ctl(struct kue_softc *sc, u_int8_t rw, u_int8_t breq, u_int16_t val, void *data, u_int16_t len) { usb_device_request_t req; if (rw == KUE_CTL_WRITE) { req.bmRequestType = UT_WRITE_VENDOR_DEVICE; } else { req.bmRequestType = UT_READ_VENDOR_DEVICE; } req.bRequest = breq; USETW(req.wValue, val); USETW(req.wIndex, 0); USETW(req.wLength, len); kue_cfg_do_request(sc, &req, data); return; } static void kue_cfg_load_fw(struct kue_softc *sc) { usb_device_descriptor_t *dd; u_int16_t hwrev; dd = &sc->sc_udev->ddesc; hwrev = UGETW(dd->bcdDevice); /* * First, check if we even need to load the firmware. * If the device was still attached when the system was * rebooted, it may already have firmware loaded in it. * If this is the case, we don't need to do it again. * And in fact, if we try to load it again, we'll hang, * so we have to avoid this condition if we don't want * to look stupid. * * We can test this quickly by checking the bcdRevision * code. The NIC will return a different revision code if * it's probed while the firmware is still loaded and * running. */ if (hwrev == 0x0202) { return; } /* load code segment */ kue_cfg_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SEND_SCAN, 0, kue_code_seg, sizeof(kue_code_seg)); /* load fixup segment */ kue_cfg_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SEND_SCAN, 0, kue_fix_seg, sizeof(kue_fix_seg)); /* send trigger command */ kue_cfg_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SEND_SCAN, 0, kue_trig_seg, sizeof(kue_trig_seg)); return; } static void kue_cfg_promisc_upd(struct kue_softc *sc, struct kue_config_copy *cc, u_int16_t refcount) { if (cc == NULL) { /* nothing to do */ return; } kue_cfg_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SET_MCAST_FILTERS, cc->if_nhash, cc->if_hash, cc->if_nhash * ETHER_ADDR_LEN); kue_cfg_setword(sc, KUE_CMD_SET_PKT_FILTER, cc->if_rxfilt); return; } static void kue_config_copy(struct kue_softc *sc, struct kue_config_copy *cc, u_int16_t refcount) { struct ifnet * ifp = sc->sc_ifp; struct ifmultiaddr * ifma; u_int16_t rxfilt = (KUE_RXFILT_UNICAST|KUE_RXFILT_BROADCAST); u_int16_t i; bzero(cc, sizeof(*cc)); if (ifp) { for (i = 0; i < ETHER_ADDR_LEN; i++) { cc->if_lladdr[i] = IF_LLADDR(ifp)[i]; } cc->if_flags = ifp->if_flags; /* if we want promiscuous mode, * set the all-frames bit: */ if (ifp->if_flags & IFF_PROMISC) { rxfilt |= KUE_RXFILT_PROMISC; } if ((ifp->if_flags & IFF_ALLMULTI) || (ifp->if_flags & IFF_PROMISC)) { rxfilt |= KUE_RXFILT_ALLMULTI; } else { i = 0; IF_ADDR_LOCK(ifp); TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) { continue; } /* * If there are too many addresses for the * internal filter, switch over to allmulti mode. */ if (i == sc->sc_mcfilt_max) { rxfilt |= KUE_RXFILT_ALLMULTI; break; } bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), cc->if_hash + (i * ETHER_ADDR_LEN), ETHER_ADDR_LEN); i++; } IF_ADDR_UNLOCK(ifp); cc->if_nhash = i; if (!(rxfilt & KUE_RXFILT_ALLMULTI)) { rxfilt |= KUE_RXFILT_MULTICAST; } } } cc->if_rxfilt = rxfilt; return; } /* * Issue a SET_CONFIGURATION command to reset the MAC. This should be * done after the firmware is loaded into the adapter in order to * bring it into proper operation. */ static void kue_cfg_reset(struct kue_softc *sc) { usbd_status err; mtx_unlock(&(sc->sc_mtx)); mtx_lock(&Giant); err = usbreq_set_config(sc->sc_udev, KUE_CONFIG_NO); mtx_unlock(&Giant); mtx_lock(&(sc->sc_mtx)); if (err) { DPRINTF(sc, 0, "reset failed (ignored)\n"); } /* wait a little while for the * chip to get its brains in order: */ err = usbd_config_td_sleep(&(sc->sc_config_td), hz/100); return; } /* * Probe for a KLSI chip. */ static int kue_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct kue_type *t; if (uaa->iface != NULL) { return(UMATCH_NONE); } t = kue_devs; while(t->kue_vid) { if ((uaa->vendor == t->kue_vid) && (uaa->product == t->kue_did)) { return UMATCH_VENDOR_PRODUCT; } t++; } return(UMATCH_NONE); } /* * Attach the interface. Allocate softc structures, do * setup and ethernet/BPF attach. */ static int kue_attach(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct kue_softc *sc = device_get_softc(dev); int32_t error; if (sc == NULL) { return ENOMEM; } sc->sc_udev = uaa->device; sc->sc_dev = dev; sc->sc_unit = device_get_unit(dev); usbd_set_desc(dev, uaa->device); mtx_init(&(sc->sc_mtx), "kue lock", NULL, MTX_DEF | MTX_RECURSE); __callout_init_mtx(&(sc->sc_watchdog), &(sc->sc_mtx), CALLOUT_RETURNUNLOCKED); error = usbd_set_config_no(uaa->device, KUE_CONFIG_NO, 0); if (error) { device_printf(dev, "setting config " "number failed!\n"); goto detach; } error = usbd_transfer_setup(uaa->device, KUE_IFACE_IDX, sc->sc_xfer, kue_config, KUE_ENDPT_MAX, sc, &(sc->sc_mtx)); if (error) { device_printf(dev, "allocating USB " "transfers failed!\n"); goto detach; } error = usbd_config_td_setup(&(sc->sc_config_td), sc, &(sc->sc_mtx), &kue_config_copy, NULL, sizeof(struct kue_config_copy), 16); if (error) { device_printf(dev, "could not setup config " "thread!\n"); goto detach; } mtx_lock(&(sc->sc_mtx)); /* start setup */ usbd_config_td_queue_command (&(sc->sc_config_td), &kue_cfg_first_time_setup, 0); /* start watchdog (will exit mutex) */ kue_watchdog(sc); return 0; /* success */ detach: kue_detach(dev); return ENXIO; /* failure */ } static void kue_cfg_first_time_setup(struct kue_softc *sc, struct kue_config_copy *cc, u_int16_t refcount) { struct ifnet * ifp; if (cc == NULL) { return; } /* load the firmware into the NIC */ kue_cfg_load_fw(sc); /* reset the adapter */ kue_cfg_reset(sc); /* read ethernet descriptor */ kue_cfg_ctl(sc, KUE_CTL_READ, KUE_CMD_GET_ETHER_DESCRIPTOR, 0, &(sc->sc_desc), sizeof(sc->sc_desc)); sc->sc_mcfilt_max = KUE_MCFILTCNT(sc); if (sc->sc_mcfilt_max > KUE_MCFILT_MAX) { sc->sc_mcfilt_max = KUE_MCFILT_MAX; } mtx_unlock(&(sc->sc_mtx)); ifp = if_alloc(IFT_ETHER); mtx_lock(&(sc->sc_mtx)); if (ifp == NULL) { printf("kue%d: could not if_alloc()\n", sc->sc_unit); goto done; } ifp->if_softc = sc; if_initname(ifp, "kue", sc->sc_unit); ifp->if_mtu = ETHERMTU; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = kue_ioctl_cb; ifp->if_start = kue_start_cb; ifp->if_watchdog = NULL; ifp->if_init = kue_init_cb; ifp->if_baudrate = 10000000; ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; sc->sc_ifp = ifp; ether_ifattach(ifp, sc->sc_desc.kue_macaddr); done: return; } static int kue_detach(device_t dev) { struct kue_softc * sc = device_get_softc(dev); struct ifnet * ifp; mtx_lock(&(sc->sc_mtx)); usbd_config_td_stop(&(sc->sc_config_td)); __callout_stop(&(sc->sc_watchdog)); kue_cfg_stop(sc, NULL, 0); ifp = sc->sc_ifp; mtx_unlock(&(sc->sc_mtx)); /* get rid of any late children */ bus_generic_detach(dev); if (ifp) { ether_ifdetach(ifp); if_free(ifp); } usbd_transfer_unsetup(sc->sc_xfer, KUE_ENDPT_MAX); usbd_config_td_unsetup(&(sc->sc_config_td)); __callout_drain(&(sc->sc_watchdog)); mtx_destroy(&(sc->sc_mtx)); return 0; } /* * A frame has been uploaded: pass the resulting mbuf chain up to * the higher level protocols. */ static void kue_bulk_read_clear_stall_callback(struct usbd_xfer *xfer) { struct kue_softc *sc = xfer->priv_sc; struct usbd_xfer *xfer_other = sc->sc_xfer[1]; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, xfer_other); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, xfer_other); sc->sc_flags &= ~KUE_FLAG_READ_STALL; usbd_transfer_start(xfer_other); return; tr_error: /* bomb out */ sc->sc_flags &= ~KUE_FLAG_READ_STALL; DPRINTF(sc, 0, "bulk read pipe stopped\n"); return; } static void kue_bulk_read_callback(struct usbd_xfer *xfer) { struct kue_softc *sc = xfer->priv_sc; struct ifnet *ifp = sc->sc_ifp; struct mbuf *m; u_int8_t buf[2]; u_int16_t len; USBD_CHECK_STATUS(xfer); tr_error: if (xfer->error != USBD_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= KUE_FLAG_READ_STALL; usbd_transfer_start(sc->sc_xfer[3]); } DPRINTF(sc, 0, "bulk read error, %s\n", usbd_errstr(xfer->error)); return; tr_transferred: if (xfer->actlen <= (2 + sizeof(struct ether_header))) { ifp->if_ierrors++; goto tr_setup; } usbd_copy_out(&(xfer->buf_data), 0, buf, 2); len = buf[0] | (buf[1] << 8); xfer->actlen -= 2; m = usbd_ether_get_mbuf(); if (m == NULL) { ifp->if_ierrors++; goto tr_setup; } xfer->actlen = min(xfer->actlen, m->m_len); xfer->actlen = min(xfer->actlen, len); usbd_copy_out(&(xfer->buf_data), 2, m->m_data, xfer->actlen); ifp->if_ipackets++; m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = m->m_len = xfer->actlen; (ifp->if_input)(ifp, m); tr_setup: if (sc->sc_flags & KUE_FLAG_READ_STALL) { usbd_transfer_start(sc->sc_xfer[3]); } else { usbd_start_hardware(xfer); } return; } static void kue_bulk_write_clear_stall_callback(struct usbd_xfer *xfer) { struct kue_softc *sc = xfer->priv_sc; struct usbd_xfer *xfer_other = sc->sc_xfer[0]; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, xfer_other); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, xfer_other); sc->sc_flags &= ~KUE_FLAG_WRITE_STALL; usbd_transfer_start(xfer_other); return; tr_error: /* bomb out */ sc->sc_flags &= ~KUE_FLAG_WRITE_STALL; DPRINTF(sc, 0, "bulk write pipe stopped\n"); return; } static void kue_bulk_write_callback(struct usbd_xfer *xfer) { struct kue_softc *sc = xfer->priv_sc; struct ifnet *ifp = sc->sc_ifp; struct mbuf *m; u_int32_t total_len; u_int8_t buf[2]; USBD_CHECK_STATUS(xfer); tr_error: DPRINTF(sc, 10, "transfer error, %s\n", usbd_errstr(xfer->error)); if (xfer->error != USBD_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= KUE_FLAG_WRITE_STALL; usbd_transfer_start(sc->sc_xfer[2]); } ifp->if_oerrors++; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; return; tr_transferred: DPRINTF(sc, 10, "transfer complete\n"); ifp->if_opackets++; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; tr_setup: if (sc->sc_flags & KUE_FLAG_WRITE_STALL) { usbd_transfer_start(sc->sc_xfer[2]); goto done; } IF_DEQUEUE(&(ifp->if_snd), m); if (m == NULL) { goto done; } if (m->m_pkthdr.len > MCLBYTES) { m->m_pkthdr.len = MCLBYTES; } xfer->length = (m->m_pkthdr.len + 2); total_len = (xfer->length + (64 - (xfer->length % 64))); /* the first two bytes are the frame length */ buf[0] = (u_int8_t)(m->m_pkthdr.len); buf[1] = (u_int8_t)(m->m_pkthdr.len >> 8); usbd_copy_in(&(xfer->buf_data), 0, buf, 2); usbd_m_copy_in(&(xfer->buf_data), 2, m, 0, m->m_pkthdr.len); usbd_bzero(&(xfer->buf_data), xfer->length, total_len - xfer->length); xfer->length = total_len; /* * if there's a BPF listener, bounce a copy * of this frame to him: */ BPF_MTAP(ifp, m); m_freem(m); usbd_start_hardware(xfer); ifp->if_drv_flags |= IFF_DRV_OACTIVE; done: return; } static void kue_start_cb(struct ifnet *ifp) { struct kue_softc * sc = ifp->if_softc; mtx_lock(&(sc->sc_mtx)); kue_start_transfers(sc); mtx_unlock(&(sc->sc_mtx)); return; } static void kue_start_transfers(struct kue_softc *sc) { if ((sc->sc_flags & KUE_FLAG_LL_READY) && (sc->sc_flags & KUE_FLAG_HL_READY)) { /* start the USB transfers, * if not already started: */ usbd_transfer_start(sc->sc_xfer[1]); usbd_transfer_start(sc->sc_xfer[0]); } return; } static void kue_init_cb(void *arg) { struct kue_softc *sc = arg; mtx_lock(&(sc->sc_mtx)); usbd_config_td_queue_command (&(sc->sc_config_td), &kue_cfg_init, 0); mtx_unlock(&(sc->sc_mtx)); return; } static void kue_cfg_init(struct kue_softc *sc, struct kue_config_copy *cc, u_int16_t refcount) { if (cc == NULL) { /* immediate configuration */ struct ifnet *ifp = sc->sc_ifp; kue_cfg_stop(sc, NULL, 0); ifp->if_drv_flags |= IFF_DRV_RUNNING; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; sc->sc_flags |= KUE_FLAG_HL_READY; return; } /* set MAC address */ kue_cfg_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SET_MAC, 0, cc->if_lladdr, ETHER_ADDR_LEN); /* I'm not sure how to tune these. */ #if 0 /* * Leave this one alone for now; setting it * wrong causes lockups on some machines/controllers. */ kue_cfg_setword(sc, KUE_CMD_SET_SOFS, 1); #endif kue_cfg_setword(sc, KUE_CMD_SET_URB_SIZE, 64); /* load the multicast filter */ kue_cfg_promisc_upd(sc, cc, 0); sc->sc_flags |= (KUE_FLAG_READ_STALL| KUE_FLAG_WRITE_STALL| KUE_FLAG_LL_READY); kue_start_transfers(sc); return; } static int kue_ioctl_cb(struct ifnet *ifp, u_long command, caddr_t data) { struct kue_softc *sc = ifp->if_softc; int error = 0; mtx_lock(&(sc->sc_mtx)); switch(command) { case SIOCSIFFLAGS: if (ifp->if_flags & IFF_UP) { if (ifp->if_drv_flags & IFF_DRV_RUNNING) { usbd_config_td_queue_command (&(sc->sc_config_td), &kue_cfg_promisc_upd, 0); } else { usbd_config_td_queue_command (&(sc->sc_config_td), &kue_cfg_init, 0); } } else { if (ifp->if_drv_flags & IFF_DRV_RUNNING) { usbd_config_td_queue_command (&(sc->sc_config_td), &kue_cfg_stop, 0); } } break; case SIOCADDMULTI: case SIOCDELMULTI: usbd_config_td_queue_command (&(sc->sc_config_td), &kue_cfg_promisc_upd, 0); break; default: error = ether_ioctl(ifp, command, data); break; } mtx_unlock(&(sc->sc_mtx)); return error; } static void kue_cfg_tick(struct kue_softc *sc, struct kue_config_copy *cc, u_int16_t refcount) { struct ifnet * ifp = sc->sc_ifp; if ((cc == NULL) || (ifp == NULL)) { /* not ready */ return; } /* start stopped transfers, if any */ kue_start_transfers(sc); return; } /* * Stop the adapter and free any mbufs allocated to the * RX and TX lists. */ static void kue_watchdog(void *arg) { struct kue_softc * sc = arg; usbd_config_td_queue_command (&(sc->sc_config_td), &kue_cfg_tick, 0); __callout_reset(&(sc->sc_watchdog), hz, &kue_watchdog, sc); mtx_unlock(&(sc->sc_mtx)); return; } static void kue_cfg_stop(struct kue_softc *sc, struct kue_config_copy *cc, u_int16_t refcount) { if (cc == NULL) { /* immediate configuration */ struct ifnet *ifp = sc->sc_ifp; if (ifp) { /* clear flags */ ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); } sc->sc_flags &= ~(KUE_FLAG_HL_READY| KUE_FLAG_LL_READY); /* stop all the transfers, * if not already stopped: */ if (sc->sc_xfer[0]) { usbd_transfer_stop(sc->sc_xfer[0]); } if (sc->sc_xfer[1]) { usbd_transfer_stop(sc->sc_xfer[1]); } if (sc->sc_xfer[2]) { usbd_transfer_stop(sc->sc_xfer[2]); } if (sc->sc_xfer[3]) { usbd_transfer_stop(sc->sc_xfer[3]); } return; } return; } /* * Stop all chip I/O so that the kernel's probe routines don't * get confused by errant DMAs when rebooting. */ static int kue_shutdown(device_t dev) { struct kue_softc *sc = device_get_softc(dev); mtx_lock(&(sc->sc_mtx)); usbd_config_td_queue_command (&(sc->sc_config_td), &kue_cfg_stop, 0); mtx_unlock(&(sc->sc_mtx)); return 0; } pwcbsd/usb/if_kuefw.h000644 000423 000000 00000103357 10551670753 015361 0ustar00luigiwheel000000 000000 /*- * Copyright (c) 1997, 1998, 1999, 2000 * Bill Paul . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD: src/sys/dev/usb/kue_fw.h,v 1.3 2005/01/06 01:43:27 imp Exp $ */ /* * This file contains the firmware needed to make the KLSI chip work, * along with a few constants related to the QT Engine microcontroller * embedded in the KLSI part. * * Firmware is loaded using the vendor-specific 'send scan data' * command (0xFF). The basic operation is that we must load the * firmware, then issue some trigger commands to fix it up and start * it running. There are three transfers: load the binary code, * load the 'fixup' (data segment?), then issue a command to * start the code firmware running. The data itself is prefixed by * a 16-bit signature word, a 16-bit length value, a type byte * and an interrupt (command) byte. The code segment is of type * 0x02 (replacement interrupt vector data) and the fixup segment * is of type 0x03 (replacement interrupt fixup data). The interrupt * code is 0x64 (load new code). The length word is the total length * of the segment minus 7. I precomputed the values and stuck them * into the appropriate locations within the segments to save some * work in the driver. */ /* QT controller data block types. */ /* Write data into specific memory location. */ #define KUE_QTBTYPE_WRITE_DATA 0x00 /* Write data into interrupt vector location */ #define KUE_QTBTYPE_WRITE_INTVEC 0x01 /* Replace interrupt vector with this data */ #define KUE_QTBTYPE_REPL_INTVEC 0x02 /* Fixup interrupt vector code with this data */ #define KUE_QTBTYPE_FIXUP_INTVEC 0x03 /* Force jump to location */ #define KUE_QTBTYPE_JUMP 0x04 /* Force call to location */ #define KUE_QTBTYPE_CALL 0x05 /* Force interrupt call */ #define KUE_QTBTYPE_CALLINTR 0x06 /* * Cause data to be written using the specified QT engine * interrupt, from starting location in memory for a specified * number of bytes. */ #define KUE_QTBTYPE_WRITE_WITH_INTR 0x07 /* Cause data from stream to be written using specified QT interrupt. */ #define KUE_QTBTYPE_WRITE_STR_WITH_INTR 0x08 /* Cause data to be written to config locations. */ /* Addresses assume 0xc000 offset. */ #define KUE_QTBTYPE_WRITE_CONFIG 0x09 #define KUE_QTINTR_LOAD_CODE 0x64 #define KUE_QTINTR_TRIGGER_CODE 0x3B #define KUE_QTINTR_LOAD_CODE_HIGH 0x9C /* Firmware code segment */ Static unsigned char kue_code_seg[] = { /******************************************/ /* NOTE: B6/C3 is data header signature */ /* 0xAA/0xBB is data length = total */ /* bytes - 7, 0xCC is type, 0xDD is */ /* interrupt to use. */ /******************************************/ 0xB6, 0xC3, 0xf7, 0x0e, 0x02, 0x64, 0x9f, 0xcf, 0xbc, 0x08, 0xe7, 0x57, 0x00, 0x00, 0x9a, 0x08, 0x97, 0xc1, 0xe7, 0x67, 0xff, 0x1f, 0x28, 0xc0, 0xe7, 0x87, 0x00, 0x04, 0x24, 0xc0, 0xe7, 0x67, 0xff, 0xf9, 0x22, 0xc0, 0x97, 0xcf, 0xe7, 0x09, 0xa2, 0xc0, 0x94, 0x08, 0xd7, 0x09, 0x00, 0xc0, 0xe7, 0x59, 0xba, 0x08, 0x94, 0x08, 0x03, 0xc1, 0xe7, 0x67, 0xff, 0xf7, 0x24, 0xc0, 0xe7, 0x05, 0x00, 0xc0, 0xa7, 0xcf, 0x92, 0x08, 0xe7, 0x57, 0x00, 0x00, 0x8e, 0x08, 0xa7, 0xa1, 0x8e, 0x08, 0x97, 0xcf, 0xe7, 0x57, 0x00, 0x00, 0xf2, 0x09, 0x0a, 0xc0, 0xe7, 0x57, 0x00, 0x00, 0xa4, 0xc0, 0xa7, 0xc0, 0x56, 0x08, 0x9f, 0xaf, 0x70, 0x09, 0xe7, 0x07, 0x00, 0x00, 0xf2, 0x09, 0xe7, 0x57, 0xff, 0xff, 0x90, 0x08, 0x9f, 0xa0, 0x40, 0x00, 0xe7, 0x59, 0x90, 0x08, 0x94, 0x08, 0x9f, 0xa0, 0x40, 0x00, 0xc8, 0x09, 0xa2, 0x08, 0x08, 0x62, 0x9f, 0xa1, 0x14, 0x0a, 0xe7, 0x57, 0x00, 0x00, 0x52, 0x08, 0xa7, 0xc0, 0x56, 0x08, 0x9f, 0xaf, 0x04, 0x00, 0xe7, 0x57, 0x00, 0x00, 0x8e, 0x08, 0xa7, 0xc1, 0x56, 0x08, 0xc0, 0x09, 0xa8, 0x08, 0x00, 0x60, 0x05, 0xc4, 0xc0, 0x59, 0x94, 0x08, 0x02, 0xc0, 0x9f, 0xaf, 0xee, 0x00, 0xe7, 0x59, 0xae, 0x08, 0x94, 0x08, 0x02, 0xc1, 0x9f, 0xaf, 0xf6, 0x00, 0x9f, 0xaf, 0x9e, 0x03, 0xef, 0x57, 0x00, 0x00, 0xf0, 0x09, 0x9f, 0xa1, 0xde, 0x01, 0xe7, 0x57, 0x00, 0x00, 0x78, 0x08, 0x9f, 0xa0, 0xe4, 0x03, 0x9f, 0xaf, 0x2c, 0x04, 0xa7, 0xcf, 0x56, 0x08, 0x48, 0x02, 0xe7, 0x09, 0x94, 0x08, 0xa8, 0x08, 0xc8, 0x37, 0x04, 0x00, 0x9f, 0xaf, 0x68, 0x04, 0x97, 0xcf, 0xe7, 0x57, 0x00, 0x00, 0xa6, 0x08, 0x97, 0xc0, 0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf, 0xc8, 0x09, 0x9c, 0x08, 0x08, 0x62, 0x1d, 0xc0, 0x27, 0x04, 0x9c, 0x08, 0x10, 0x94, 0xf0, 0x07, 0xee, 0x09, 0x02, 0x00, 0xc1, 0x07, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00, 0xf0, 0x07, 0x44, 0x01, 0x06, 0x00, 0x50, 0xaf, 0xe7, 0x09, 0x94, 0x08, 0xae, 0x08, 0xe7, 0x17, 0x14, 0x00, 0xae, 0x08, 0xe7, 0x67, 0xff, 0x07, 0xae, 0x08, 0xe7, 0x07, 0xff, 0xff, 0xa8, 0x08, 0xe7, 0x07, 0x00, 0x00, 0xa6, 0x08, 0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf, 0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf, 0x48, 0x02, 0xd0, 0x09, 0x9c, 0x08, 0x27, 0x02, 0x9c, 0x08, 0xe7, 0x09, 0x20, 0xc0, 0xee, 0x09, 0xe7, 0xd0, 0xee, 0x09, 0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf, 0x48, 0x02, 0xc8, 0x37, 0x04, 0x00, 0x00, 0x0c, 0x0c, 0x00, 0x00, 0x60, 0x21, 0xc0, 0xc0, 0x37, 0x3e, 0x00, 0x23, 0xc9, 0xc0, 0x57, 0xb4, 0x05, 0x1b, 0xc8, 0xc0, 0x17, 0x3f, 0x00, 0xc0, 0x67, 0xc0, 0xff, 0x30, 0x00, 0x08, 0x00, 0xf0, 0x07, 0x00, 0x00, 0x04, 0x00, 0x00, 0x02, 0xc0, 0x17, 0x4c, 0x00, 0x30, 0x00, 0x06, 0x00, 0xf0, 0x07, 0xbe, 0x01, 0x0a, 0x00, 0x48, 0x02, 0xc1, 0x07, 0x02, 0x00, 0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf, 0x51, 0xaf, 0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf, 0x9f, 0xaf, 0x68, 0x04, 0x9f, 0xaf, 0xe4, 0x03, 0x97, 0xcf, 0x9f, 0xaf, 0xe4, 0x03, 0xc9, 0x37, 0x04, 0x00, 0xc1, 0xdf, 0xc8, 0x09, 0x70, 0x08, 0x50, 0x02, 0x67, 0x02, 0x70, 0x08, 0xd1, 0x07, 0x00, 0x00, 0xc0, 0xdf, 0x9f, 0xaf, 0xde, 0x01, 0x97, 0xcf, 0xe7, 0x57, 0x00, 0x00, 0xaa, 0x08, 0x97, 0xc1, 0xe7, 0x57, 0x01, 0x00, 0x7a, 0x08, 0x97, 0xc0, 0xc8, 0x09, 0x6e, 0x08, 0x08, 0x62, 0x97, 0xc0, 0x00, 0x02, 0xc0, 0x17, 0x0e, 0x00, 0x27, 0x00, 0x34, 0x01, 0x27, 0x0c, 0x0c, 0x00, 0x36, 0x01, 0xef, 0x57, 0x00, 0x00, 0xf0, 0x09, 0x9f, 0xc0, 0xbe, 0x02, 0xe7, 0x57, 0x00, 0x00, 0xb0, 0x08, 0x97, 0xc1, 0xe7, 0x07, 0x09, 0x00, 0x12, 0xc0, 0xe7, 0x77, 0x00, 0x08, 0x20, 0xc0, 0x9f, 0xc1, 0xb6, 0x02, 0xe7, 0x57, 0x09, 0x00, 0x12, 0xc0, 0x77, 0xc9, 0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf, 0xe7, 0x77, 0x00, 0x08, 0x20, 0xc0, 0x2f, 0xc1, 0xe7, 0x07, 0x00, 0x00, 0x42, 0xc0, 0xe7, 0x07, 0x05, 0x00, 0x90, 0xc0, 0xc8, 0x07, 0x0a, 0x00, 0xe7, 0x77, 0x04, 0x00, 0x20, 0xc0, 0x09, 0xc1, 0x08, 0xda, 0x7a, 0xc1, 0xe7, 0x07, 0x00, 0x01, 0x42, 0xc0, 0xe7, 0x07, 0x04, 0x00, 0x90, 0xc0, 0x1a, 0xcf, 0xe7, 0x07, 0x01, 0x00, 0x7a, 0x08, 0x00, 0xd8, 0x27, 0x50, 0x34, 0x01, 0x17, 0xc1, 0xe7, 0x77, 0x02, 0x00, 0x20, 0xc0, 0x79, 0xc1, 0x27, 0x50, 0x34, 0x01, 0x10, 0xc1, 0xe7, 0x77, 0x02, 0x00, 0x20, 0xc0, 0x79, 0xc0, 0x9f, 0xaf, 0xd8, 0x02, 0xe7, 0x05, 0x00, 0xc0, 0x00, 0x60, 0x9f, 0xc0, 0xde, 0x01, 0x97, 0xcf, 0xe7, 0x07, 0x01, 0x00, 0xb8, 0x08, 0x06, 0xcf, 0xe7, 0x07, 0x30, 0x0e, 0x02, 0x00, 0xe7, 0x07, 0x50, 0xc3, 0x12, 0xc0, 0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf, 0xe7, 0x07, 0x01, 0x00, 0xb8, 0x08, 0x97, 0xcf, 0xe7, 0x07, 0x50, 0xc3, 0x12, 0xc0, 0xe7, 0x07, 0x30, 0x0e, 0x02, 0x00, 0xe7, 0x07, 0x01, 0x00, 0x7a, 0x08, 0xe7, 0x07, 0x05, 0x00, 0x90, 0xc0, 0x97, 0xcf, 0xe7, 0x07, 0x00, 0x01, 0x42, 0xc0, 0xe7, 0x07, 0x04, 0x00, 0x90, 0xc0, 0xe7, 0x07, 0x00, 0x00, 0x7a, 0x08, 0xe7, 0x57, 0x0f, 0x00, 0xb2, 0x08, 0x13, 0xc1, 0x9f, 0xaf, 0x2e, 0x08, 0xca, 0x09, 0xac, 0x08, 0xf2, 0x17, 0x01, 0x00, 0x5c, 0x00, 0xf2, 0x27, 0x00, 0x00, 0x5e, 0x00, 0xe7, 0x07, 0x00, 0x00, 0xb2, 0x08, 0xe7, 0x07, 0x01, 0x00, 0xb4, 0x08, 0xc0, 0x07, 0xff, 0xff, 0x97, 0xcf, 0x9f, 0xaf, 0x4c, 0x03, 0xc0, 0x69, 0xb4, 0x08, 0x57, 0x00, 0x9f, 0xde, 0x33, 0x00, 0xc1, 0x05, 0x27, 0xd8, 0xb2, 0x08, 0x27, 0xd2, 0xb4, 0x08, 0xe7, 0x87, 0x01, 0x00, 0xb4, 0x08, 0xe7, 0x67, 0xff, 0x03, 0xb4, 0x08, 0x00, 0x60, 0x97, 0xc0, 0xe7, 0x07, 0x01, 0x00, 0xb0, 0x08, 0x27, 0x00, 0x12, 0xc0, 0x97, 0xcf, 0xc0, 0x09, 0xb6, 0x08, 0x00, 0xd2, 0x02, 0xc3, 0xc0, 0x97, 0x05, 0x80, 0x27, 0x00, 0xb6, 0x08, 0xc0, 0x99, 0x82, 0x08, 0xc0, 0x99, 0xa2, 0xc0, 0x97, 0xcf, 0xe7, 0x07, 0x00, 0x00, 0xb0, 0x08, 0xc0, 0xdf, 0x97, 0xcf, 0xc8, 0x09, 0x72, 0x08, 0x08, 0x62, 0x02, 0xc0, 0x10, 0x64, 0x07, 0xc1, 0xe7, 0x07, 0x00, 0x00, 0x64, 0x08, 0xe7, 0x07, 0xc8, 0x05, 0x24, 0x00, 0x97, 0xcf, 0x27, 0x04, 0x72, 0x08, 0xc8, 0x17, 0x0e, 0x00, 0x27, 0x02, 0x64, 0x08, 0xe7, 0x07, 0xd6, 0x05, 0x24, 0x00, 0x97, 0xcf, 0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf, 0xe7, 0x57, 0x00, 0x00, 0x62, 0x08, 0x13, 0xc1, 0x9f, 0xaf, 0x70, 0x03, 0xe7, 0x57, 0x00, 0x00, 0x64, 0x08, 0x13, 0xc0, 0xe7, 0x09, 0x64, 0x08, 0x30, 0x01, 0xe7, 0x07, 0xf2, 0x05, 0x32, 0x01, 0xe7, 0x07, 0x10, 0x00, 0x96, 0xc0, 0xe7, 0x09, 0x64, 0x08, 0x62, 0x08, 0x04, 0xcf, 0xe7, 0x57, 0x00, 0x00, 0x64, 0x08, 0x02, 0xc1, 0x9f, 0xaf, 0x70, 0x03, 0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf, 0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf, 0xc8, 0x09, 0x72, 0x08, 0x27, 0x02, 0x78, 0x08, 0x08, 0x62, 0x03, 0xc1, 0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf, 0x27, 0x04, 0x72, 0x08, 0xe7, 0x05, 0x00, 0xc0, 0xf0, 0x07, 0x40, 0x00, 0x08, 0x00, 0xf0, 0x07, 0x00, 0x00, 0x04, 0x00, 0x00, 0x02, 0xc0, 0x17, 0x0c, 0x00, 0x30, 0x00, 0x06, 0x00, 0xf0, 0x07, 0x64, 0x01, 0x0a, 0x00, 0xc8, 0x17, 0x04, 0x00, 0xc1, 0x07, 0x02, 0x00, 0x51, 0xaf, 0x97, 0xcf, 0xe7, 0x57, 0x00, 0x00, 0x6a, 0x08, 0x97, 0xc0, 0xc1, 0xdf, 0xc8, 0x09, 0x6a, 0x08, 0x27, 0x04, 0x6a, 0x08, 0x27, 0x52, 0x6c, 0x08, 0x03, 0xc1, 0xe7, 0x07, 0x6a, 0x08, 0x6c, 0x08, 0xc0, 0xdf, 0x17, 0x02, 0xc8, 0x17, 0x0e, 0x00, 0x9f, 0xaf, 0x16, 0x05, 0xc8, 0x05, 0x00, 0x60, 0x03, 0xc0, 0x9f, 0xaf, 0x80, 0x04, 0x97, 0xcf, 0x9f, 0xaf, 0x68, 0x04, 0x97, 0xcf, 0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf, 0x08, 0x62, 0x1c, 0xc0, 0xd0, 0x09, 0x72, 0x08, 0x27, 0x02, 0x72, 0x08, 0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf, 0x97, 0x02, 0xca, 0x09, 0xac, 0x08, 0xf2, 0x17, 0x01, 0x00, 0x04, 0x00, 0xf2, 0x27, 0x00, 0x00, 0x06, 0x00, 0xca, 0x17, 0x2c, 0x00, 0xf8, 0x77, 0x01, 0x00, 0x0e, 0x00, 0x06, 0xc0, 0xca, 0xd9, 0xf8, 0x57, 0xff, 0x00, 0x0e, 0x00, 0x01, 0xc1, 0xca, 0xd9, 0x22, 0x1c, 0x0c, 0x00, 0xe2, 0x27, 0x00, 0x00, 0xe2, 0x17, 0x01, 0x00, 0xe2, 0x27, 0x00, 0x00, 0xca, 0x05, 0x00, 0x0c, 0x0c, 0x00, 0xc0, 0x17, 0x41, 0x00, 0xc0, 0x67, 0xc0, 0xff, 0x30, 0x00, 0x08, 0x00, 0x00, 0x02, 0xc0, 0x17, 0x0c, 0x00, 0x30, 0x00, 0x06, 0x00, 0xf0, 0x07, 0xdc, 0x00, 0x0a, 0x00, 0xf0, 0x07, 0x00, 0x00, 0x04, 0x00, 0x00, 0x0c, 0x08, 0x00, 0x40, 0xd1, 0x01, 0x00, 0xc0, 0x19, 0xa6, 0x08, 0xc0, 0x59, 0x98, 0x08, 0x04, 0xc9, 0x49, 0xaf, 0x9f, 0xaf, 0xee, 0x00, 0x4a, 0xaf, 0x67, 0x10, 0xa6, 0x08, 0xc8, 0x17, 0x04, 0x00, 0xc1, 0x07, 0x01, 0x00, 0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf, 0x50, 0xaf, 0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf, 0xc0, 0x07, 0x01, 0x00, 0xc1, 0x09, 0x7c, 0x08, 0xc1, 0x77, 0x01, 0x00, 0x97, 0xc1, 0xd8, 0x77, 0x01, 0x00, 0x12, 0xc0, 0xc9, 0x07, 0x4c, 0x08, 0x9f, 0xaf, 0x64, 0x05, 0x04, 0xc1, 0xc1, 0x77, 0x08, 0x00, 0x13, 0xc0, 0x97, 0xcf, 0xc1, 0x77, 0x02, 0x00, 0x97, 0xc1, 0xc1, 0x77, 0x10, 0x00, 0x0c, 0xc0, 0x9f, 0xaf, 0x86, 0x05, 0x97, 0xcf, 0xc1, 0x77, 0x04, 0x00, 0x06, 0xc0, 0xc9, 0x07, 0x7e, 0x08, 0x9f, 0xaf, 0x64, 0x05, 0x97, 0xc0, 0x00, 0xcf, 0x00, 0x90, 0x97, 0xcf, 0x50, 0x54, 0x97, 0xc1, 0x70, 0x5c, 0x02, 0x00, 0x02, 0x00, 0x97, 0xc1, 0x70, 0x5c, 0x04, 0x00, 0x04, 0x00, 0x97, 0xcf, 0xc0, 0x00, 0x60, 0x00, 0x30, 0x00, 0x18, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x00, 0x00, 0xcb, 0x09, 0x88, 0x08, 0xcc, 0x09, 0x8a, 0x08, 0x0b, 0x53, 0x11, 0xc0, 0xc9, 0x02, 0xca, 0x07, 0x78, 0x05, 0x9f, 0xaf, 0x64, 0x05, 0x97, 0xc0, 0x0a, 0xc8, 0x82, 0x08, 0x0a, 0xcf, 0x82, 0x08, 0x9f, 0xaf, 0x64, 0x05, 0x97, 0xc0, 0x05, 0xc2, 0x89, 0x30, 0x82, 0x60, 0x78, 0xc1, 0x00, 0x90, 0x97, 0xcf, 0x89, 0x10, 0x09, 0x53, 0x79, 0xc2, 0x89, 0x30, 0x82, 0x08, 0x7a, 0xcf, 0xc0, 0xdf, 0x97, 0xcf, 0xe7, 0x09, 0x96, 0xc0, 0x66, 0x08, 0xe7, 0x09, 0x98, 0xc0, 0x68, 0x08, 0x0f, 0xcf, 0xe7, 0x09, 0x96, 0xc0, 0x66, 0x08, 0xe7, 0x09, 0x98, 0xc0, 0x68, 0x08, 0xe7, 0x09, 0x64, 0x08, 0x30, 0x01, 0xe7, 0x07, 0xf2, 0x05, 0x32, 0x01, 0xe7, 0x07, 0x10, 0x00, 0x96, 0xc0, 0xd7, 0x09, 0x00, 0xc0, 0x17, 0x02, 0xc8, 0x09, 0x62, 0x08, 0xc8, 0x37, 0x0e, 0x00, 0xe7, 0x57, 0x04, 0x00, 0x68, 0x08, 0x3d, 0xc0, 0xe7, 0x87, 0x00, 0x08, 0x24, 0xc0, 0xe7, 0x09, 0x94, 0x08, 0xba, 0x08, 0xe7, 0x17, 0x64, 0x00, 0xba, 0x08, 0xe7, 0x67, 0xff, 0x07, 0xba, 0x08, 0xe7, 0x77, 0x2a, 0x00, 0x66, 0x08, 0x30, 0xc0, 0x97, 0x02, 0xca, 0x09, 0xac, 0x08, 0xe7, 0x77, 0x20, 0x00, 0x66, 0x08, 0x0e, 0xc0, 0xf2, 0x17, 0x01, 0x00, 0x10, 0x00, 0xf2, 0x27, 0x00, 0x00, 0x12, 0x00, 0xe7, 0x77, 0x0a, 0x00, 0x66, 0x08, 0xca, 0x05, 0x1e, 0xc0, 0x97, 0x02, 0xca, 0x09, 0xac, 0x08, 0xf2, 0x17, 0x01, 0x00, 0x0c, 0x00, 0xf2, 0x27, 0x00, 0x00, 0x0e, 0x00, 0xe7, 0x77, 0x02, 0x00, 0x66, 0x08, 0x07, 0xc0, 0xf2, 0x17, 0x01, 0x00, 0x44, 0x00, 0xf2, 0x27, 0x00, 0x00, 0x46, 0x00, 0x06, 0xcf, 0xf2, 0x17, 0x01, 0x00, 0x60, 0x00, 0xf2, 0x27, 0x00, 0x00, 0x62, 0x00, 0xca, 0x05, 0x9f, 0xaf, 0x68, 0x04, 0x0f, 0xcf, 0x57, 0x02, 0x09, 0x02, 0xf1, 0x09, 0x68, 0x08, 0x0c, 0x00, 0xf1, 0xda, 0x0c, 0x00, 0xc8, 0x09, 0x6c, 0x08, 0x50, 0x02, 0x67, 0x02, 0x6c, 0x08, 0xd1, 0x07, 0x00, 0x00, 0xc9, 0x05, 0xe7, 0x09, 0x64, 0x08, 0x62, 0x08, 0xe7, 0x57, 0x00, 0x00, 0x62, 0x08, 0x02, 0xc0, 0x9f, 0xaf, 0x70, 0x03, 0xc8, 0x05, 0xe7, 0x05, 0x00, 0xc0, 0xc0, 0xdf, 0x97, 0xcf, 0xd7, 0x09, 0x00, 0xc0, 0x17, 0x00, 0x17, 0x02, 0x97, 0x02, 0xc0, 0x09, 0x92, 0xc0, 0xe7, 0x87, 0x00, 0x08, 0x24, 0xc0, 0xe7, 0x09, 0x94, 0x08, 0xba, 0x08, 0xe7, 0x17, 0x64, 0x00, 0xba, 0x08, 0xe7, 0x67, 0xff, 0x07, 0xba, 0x08, 0xe7, 0x07, 0x04, 0x00, 0x90, 0xc0, 0xca, 0x09, 0xac, 0x08, 0xe7, 0x07, 0x00, 0x00, 0x7a, 0x08, 0xe7, 0x07, 0x66, 0x03, 0x02, 0x00, 0xc0, 0x77, 0x02, 0x00, 0x10, 0xc0, 0xef, 0x57, 0x00, 0x00, 0xf0, 0x09, 0x04, 0xc0, 0x9f, 0xaf, 0xd8, 0x02, 0x9f, 0xcf, 0x12, 0x08, 0xf2, 0x17, 0x01, 0x00, 0x50, 0x00, 0xf2, 0x27, 0x00, 0x00, 0x52, 0x00, 0x9f, 0xcf, 0x12, 0x08, 0xef, 0x57, 0x00, 0x00, 0xf0, 0x09, 0x08, 0xc0, 0xe7, 0x57, 0x00, 0x00, 0xb8, 0x08, 0xe7, 0x07, 0x00, 0x00, 0xb8, 0x08, 0x0a, 0xc0, 0x03, 0xcf, 0xc0, 0x77, 0x10, 0x00, 0x06, 0xc0, 0xf2, 0x17, 0x01, 0x00, 0x58, 0x00, 0xf2, 0x27, 0x00, 0x00, 0x5a, 0x00, 0xc0, 0x77, 0x80, 0x00, 0x06, 0xc0, 0xf2, 0x17, 0x01, 0x00, 0x70, 0x00, 0xf2, 0x27, 0x00, 0x00, 0x72, 0x00, 0xc0, 0x77, 0x08, 0x00, 0x1d, 0xc1, 0xf2, 0x17, 0x01, 0x00, 0x08, 0x00, 0xf2, 0x27, 0x00, 0x00, 0x0a, 0x00, 0xc0, 0x77, 0x00, 0x02, 0x06, 0xc0, 0xf2, 0x17, 0x01, 0x00, 0x64, 0x00, 0xf2, 0x27, 0x00, 0x00, 0x66, 0x00, 0xc0, 0x77, 0x40, 0x00, 0x06, 0xc0, 0xf2, 0x17, 0x01, 0x00, 0x5c, 0x00, 0xf2, 0x27, 0x00, 0x00, 0x5e, 0x00, 0xc0, 0x77, 0x01, 0x00, 0x01, 0xc0, 0x37, 0xcf, 0x36, 0xcf, 0xf2, 0x17, 0x01, 0x00, 0x00, 0x00, 0xf2, 0x27, 0x00, 0x00, 0x02, 0x00, 0xef, 0x57, 0x00, 0x00, 0xf0, 0x09, 0x18, 0xc0, 0xe7, 0x57, 0x01, 0x00, 0xb2, 0x08, 0x0e, 0xc2, 0x07, 0xc8, 0xf2, 0x17, 0x01, 0x00, 0x50, 0x00, 0xf2, 0x27, 0x00, 0x00, 0x52, 0x00, 0x06, 0xcf, 0xf2, 0x17, 0x01, 0x00, 0x54, 0x00, 0xf2, 0x27, 0x00, 0x00, 0x56, 0x00, 0xe7, 0x07, 0x00, 0x00, 0xb2, 0x08, 0xe7, 0x07, 0x01, 0x00, 0xb4, 0x08, 0xc8, 0x09, 0x34, 0x01, 0xca, 0x17, 0x14, 0x00, 0xd8, 0x77, 0x01, 0x00, 0x05, 0xc0, 0xca, 0xd9, 0xd8, 0x57, 0xff, 0x00, 0x01, 0xc0, 0xca, 0xd9, 0xe2, 0x19, 0x94, 0xc0, 0xe2, 0x27, 0x00, 0x00, 0xe2, 0x17, 0x01, 0x00, 0xe2, 0x27, 0x00, 0x00, 0x9f, 0xaf, 0x2e, 0x08, 0x9f, 0xaf, 0xde, 0x01, 0xe7, 0x57, 0x00, 0x00, 0xaa, 0x08, 0x9f, 0xa1, 0xf0, 0x0b, 0xca, 0x05, 0xc8, 0x05, 0xc0, 0x05, 0xe7, 0x05, 0x00, 0xc0, 0xc0, 0xdf, 0x97, 0xcf, 0xc8, 0x09, 0x6e, 0x08, 0x08, 0x62, 0x97, 0xc0, 0x27, 0x04, 0x6e, 0x08, 0x27, 0x52, 0x70, 0x08, 0x03, 0xc1, 0xe7, 0x07, 0x6e, 0x08, 0x70, 0x08, 0x9f, 0xaf, 0x68, 0x04, 0x97, 0xcf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x33, 0xcc, 0x00, 0x00, 0x00, 0x00, 0xe7, 0x57, 0x00, 0x80, 0xb2, 0x00, 0x06, 0xc2, 0xe7, 0x07, 0x52, 0x0e, 0x12, 0x00, 0xe7, 0x07, 0x98, 0x0e, 0xb2, 0x00, 0xe7, 0x07, 0xa4, 0x09, 0xf2, 0x02, 0xc8, 0x09, 0xb4, 0x00, 0xf8, 0x07, 0x02, 0x00, 0x0d, 0x00, 0xd7, 0x09, 0x0e, 0xc0, 0xe7, 0x07, 0x00, 0x00, 0x0e, 0xc0, 0xc8, 0x09, 0xdc, 0x00, 0xf0, 0x07, 0xff, 0xff, 0x09, 0x00, 0xf0, 0x07, 0xfb, 0x13, 0x0b, 0x00, 0xe7, 0x09, 0xc0, 0x00, 0x58, 0x08, 0xe7, 0x09, 0xbe, 0x00, 0x54, 0x08, 0xe7, 0x09, 0x10, 0x00, 0x92, 0x08, 0xc8, 0x07, 0xb4, 0x09, 0x9f, 0xaf, 0x8c, 0x09, 0x9f, 0xaf, 0xe2, 0x0b, 0xc0, 0x07, 0x80, 0x01, 0x44, 0xaf, 0x27, 0x00, 0x88, 0x08, 0x27, 0x00, 0x8a, 0x08, 0x27, 0x00, 0x8c, 0x08, 0xc0, 0x07, 0x74, 0x00, 0x44, 0xaf, 0x27, 0x00, 0xac, 0x08, 0x08, 0x00, 0x00, 0x90, 0xc1, 0x07, 0x1d, 0x00, 0x20, 0x00, 0x20, 0x00, 0x01, 0xda, 0x7c, 0xc1, 0x9f, 0xaf, 0x8a, 0x0b, 0xc0, 0x07, 0x4c, 0x00, 0x48, 0xaf, 0x27, 0x00, 0x56, 0x08, 0x9f, 0xaf, 0x72, 0x0c, 0xe7, 0x07, 0x00, 0x80, 0x96, 0x08, 0xef, 0x57, 0x00, 0x00, 0xf0, 0x09, 0x03, 0xc0, 0xe7, 0x07, 0x01, 0x00, 0x1c, 0xc0, 0xe7, 0x05, 0x0e, 0xc0, 0x97, 0xcf, 0x49, 0xaf, 0xe7, 0x87, 0x43, 0x00, 0x0e, 0xc0, 0xe7, 0x07, 0xff, 0xff, 0x94, 0x08, 0x9f, 0xaf, 0x8a, 0x0c, 0xc0, 0x07, 0x01, 0x00, 0x60, 0xaf, 0x4a, 0xaf, 0x97, 0xcf, 0x00, 0x08, 0x09, 0x08, 0x11, 0x08, 0x00, 0xda, 0x7c, 0xc1, 0x97, 0xcf, 0x67, 0x04, 0xcc, 0x02, 0xc0, 0xdf, 0x51, 0x94, 0xb1, 0xaf, 0x06, 0x00, 0xc1, 0xdf, 0xc9, 0x09, 0xcc, 0x02, 0x49, 0x62, 0x75, 0xc1, 0xc0, 0xdf, 0xa7, 0xcf, 0xd6, 0x02, 0x0e, 0x00, 0x24, 0x00, 0xd6, 0x05, 0x22, 0x00, 0xc4, 0x06, 0xd0, 0x00, 0xf0, 0x0b, 0xaa, 0x00, 0x0e, 0x0a, 0xbe, 0x00, 0x2c, 0x0c, 0x10, 0x00, 0x20, 0x00, 0x04, 0x00, 0xc4, 0x05, 0x02, 0x00, 0x66, 0x03, 0x06, 0x00, 0x00, 0x00, 0x24, 0xc0, 0x04, 0x04, 0x28, 0xc0, 0xfe, 0xfb, 0x1e, 0xc0, 0x00, 0x04, 0x22, 0xc0, 0xff, 0xf0, 0xc0, 0x00, 0x60, 0x0b, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x34, 0x0a, 0x3e, 0x0a, 0x9e, 0x0a, 0xa8, 0x0a, 0xce, 0x0a, 0xd2, 0x0a, 0xd6, 0x0a, 0x00, 0x0b, 0x10, 0x0b, 0x1e, 0x0b, 0x20, 0x0b, 0x28, 0x0b, 0x28, 0x0b, 0x27, 0x02, 0xa2, 0x08, 0x97, 0xcf, 0xe7, 0x07, 0x00, 0x00, 0xa2, 0x08, 0x0a, 0x0e, 0x01, 0x00, 0xca, 0x57, 0x0e, 0x00, 0x9f, 0xc3, 0x2a, 0x0b, 0xca, 0x37, 0x00, 0x00, 0x9f, 0xc2, 0x2a, 0x0b, 0x0a, 0xd2, 0xb2, 0xcf, 0xf4, 0x09, 0xc8, 0x09, 0xde, 0x00, 0x07, 0x06, 0x9f, 0xcf, 0x3c, 0x0b, 0xf0, 0x57, 0x80, 0x01, 0x06, 0x00, 0x9f, 0xc8, 0x2a, 0x0b, 0x27, 0x0c, 0x02, 0x00, 0x86, 0x08, 0xc0, 0x09, 0x88, 0x08, 0x27, 0x00, 0x8a, 0x08, 0xe7, 0x07, 0x00, 0x00, 0x84, 0x08, 0x27, 0x00, 0x5c, 0x08, 0x00, 0x1c, 0x06, 0x00, 0x27, 0x00, 0x8c, 0x08, 0x41, 0x90, 0x67, 0x50, 0x86, 0x08, 0x0d, 0xc0, 0x67, 0x00, 0x5a, 0x08, 0x27, 0x0c, 0x06, 0x00, 0x5e, 0x08, 0xe7, 0x07, 0x8a, 0x0a, 0x60, 0x08, 0xc8, 0x07, 0x5a, 0x08, 0x41, 0x90, 0x51, 0xaf, 0x97, 0xcf, 0x9f, 0xaf, 0xac, 0x0e, 0xe7, 0x09, 0x8c, 0x08, 0x8a, 0x08, 0xe7, 0x09, 0x86, 0x08, 0x84, 0x08, 0x59, 0xaf, 0x97, 0xcf, 0x27, 0x0c, 0x02, 0x00, 0x7c, 0x08, 0x59, 0xaf, 0x97, 0xcf, 0x09, 0x0c, 0x02, 0x00, 0x09, 0xda, 0x49, 0xd2, 0xc9, 0x19, 0xac, 0x08, 0xc8, 0x07, 0x5a, 0x08, 0xe0, 0x07, 0x00, 0x00, 0x60, 0x02, 0xe0, 0x07, 0x04, 0x00, 0xd0, 0x07, 0x9a, 0x0a, 0x48, 0xdb, 0x41, 0x90, 0x50, 0xaf, 0x97, 0xcf, 0x59, 0xaf, 0x97, 0xcf, 0x59, 0xaf, 0x97, 0xcf, 0xf0, 0x57, 0x06, 0x00, 0x06, 0x00, 0x26, 0xc1, 0xe7, 0x07, 0x7e, 0x08, 0x5c, 0x08, 0x41, 0x90, 0x67, 0x00, 0x5a, 0x08, 0x27, 0x0c, 0x06, 0x00, 0x5e, 0x08, 0xe7, 0x07, 0x5c, 0x0b, 0x60, 0x08, 0xc8, 0x07, 0x5a, 0x08, 0x41, 0x90, 0x51, 0xaf, 0x97, 0xcf, 0x07, 0x0c, 0x06, 0x00, 0xc7, 0x57, 0x06, 0x00, 0x10, 0xc1, 0xc8, 0x07, 0x7e, 0x08, 0x16, 0xcf, 0x00, 0x0c, 0x02, 0x00, 0x00, 0xda, 0x40, 0xd1, 0x27, 0x00, 0x98, 0x08, 0x1f, 0xcf, 0x1e, 0xcf, 0x27, 0x0c, 0x02, 0x00, 0xa4, 0x08, 0x1a, 0xcf, 0x00, 0xcf, 0x27, 0x02, 0x20, 0x01, 0xe7, 0x07, 0x08, 0x00, 0x22, 0x01, 0xe7, 0x07, 0x13, 0x00, 0xb0, 0xc0, 0x97, 0xcf, 0x41, 0x90, 0x67, 0x00, 0x5a, 0x08, 0xe7, 0x01, 0x5e, 0x08, 0x27, 0x02, 0x5c, 0x08, 0xe7, 0x07, 0x5c, 0x0b, 0x60, 0x08, 0xc8, 0x07, 0x5a, 0x08, 0xc1, 0x07, 0x00, 0x80, 0x50, 0xaf, 0x97, 0xcf, 0x59, 0xaf, 0x97, 0xcf, 0x00, 0x60, 0x05, 0xc0, 0xe7, 0x07, 0x00, 0x00, 0x9a, 0x08, 0xa7, 0xcf, 0x58, 0x08, 0x9f, 0xaf, 0xe2, 0x0b, 0xe7, 0x07, 0x01, 0x00, 0x9a, 0x08, 0x49, 0xaf, 0xd7, 0x09, 0x00, 0xc0, 0x07, 0xaf, 0xe7, 0x05, 0x00, 0xc0, 0x4a, 0xaf, 0xa7, 0xcf, 0x58, 0x08, 0xc0, 0x07, 0x40, 0x00, 0x44, 0xaf, 0x27, 0x00, 0xa0, 0x08, 0x08, 0x00, 0xc0, 0x07, 0x20, 0x00, 0x20, 0x94, 0x00, 0xda, 0x7d, 0xc1, 0xc0, 0x07, 0xfe, 0x7f, 0x44, 0xaf, 0x40, 0x00, 0x41, 0x90, 0xc0, 0x37, 0x08, 0x00, 0xdf, 0xde, 0x50, 0x06, 0xc0, 0x57, 0x10, 0x00, 0x02, 0xc2, 0xc0, 0x07, 0x10, 0x00, 0x27, 0x00, 0x76, 0x08, 0x41, 0x90, 0x9f, 0xde, 0x40, 0x06, 0x44, 0xaf, 0x27, 0x00, 0x74, 0x08, 0xc0, 0x09, 0x76, 0x08, 0x41, 0x90, 0x00, 0xd2, 0x00, 0xd8, 0x9f, 0xde, 0x08, 0x00, 0x44, 0xaf, 0x27, 0x00, 0x9e, 0x08, 0x97, 0xcf, 0xe7, 0x87, 0x00, 0x84, 0x28, 0xc0, 0xe7, 0x67, 0xff, 0xf3, 0x24, 0xc0, 0x97, 0xcf, 0xe7, 0x87, 0x01, 0x00, 0xaa, 0x08, 0xe7, 0x57, 0x00, 0x00, 0x7a, 0x08, 0x97, 0xc1, 0x9f, 0xaf, 0xe2, 0x0b, 0xe7, 0x87, 0x00, 0x06, 0x22, 0xc0, 0xe7, 0x07, 0x00, 0x00, 0x90, 0xc0, 0xe7, 0x67, 0xfe, 0xff, 0x3e, 0xc0, 0xe7, 0x07, 0x2e, 0x00, 0x0a, 0xc0, 0xe7, 0x87, 0x01, 0x00, 0x3e, 0xc0, 0xe7, 0x07, 0xff, 0xff, 0x94, 0x08, 0x9f, 0xaf, 0xf0, 0x0c, 0x97, 0xcf, 0x17, 0x00, 0xa7, 0xaf, 0x54, 0x08, 0xc0, 0x05, 0x27, 0x00, 0x52, 0x08, 0xe7, 0x87, 0x01, 0x00, 0xaa, 0x08, 0x9f, 0xaf, 0xe2, 0x0b, 0xe7, 0x07, 0x0c, 0x00, 0x40, 0xc0, 0x9f, 0xaf, 0xf0, 0x0c, 0xe7, 0x07, 0x00, 0x00, 0x78, 0x08, 0x00, 0x90, 0xe7, 0x09, 0x88, 0x08, 0x8a, 0x08, 0x27, 0x00, 0x84, 0x08, 0x27, 0x00, 0x7c, 0x08, 0x9f, 0xaf, 0x8a, 0x0c, 0xe7, 0x07, 0x00, 0x00, 0xb2, 0x02, 0xe7, 0x07, 0x00, 0x00, 0xb4, 0x02, 0xc0, 0x07, 0x06, 0x00, 0xc8, 0x09, 0xde, 0x00, 0xc8, 0x17, 0x03, 0x00, 0xc9, 0x07, 0x7e, 0x08, 0x29, 0x0a, 0x00, 0xda, 0x7d, 0xc1, 0x97, 0xcf, 0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf, 0x00, 0x90, 0x27, 0x00, 0x6a, 0x08, 0xe7, 0x07, 0x6a, 0x08, 0x6c, 0x08, 0x27, 0x00, 0x6e, 0x08, 0xe7, 0x07, 0x6e, 0x08, 0x70, 0x08, 0x27, 0x00, 0x78, 0x08, 0x27, 0x00, 0x62, 0x08, 0x27, 0x00, 0x64, 0x08, 0xc8, 0x09, 0x74, 0x08, 0xc1, 0x09, 0x76, 0x08, 0xc9, 0x07, 0x72, 0x08, 0x11, 0x02, 0x09, 0x02, 0xc8, 0x17, 0x40, 0x06, 0x01, 0xda, 0x7a, 0xc1, 0x51, 0x94, 0xc8, 0x09, 0x9e, 0x08, 0xc9, 0x07, 0x9c, 0x08, 0xc1, 0x09, 0x76, 0x08, 0x01, 0xd2, 0x01, 0xd8, 0x11, 0x02, 0x09, 0x02, 0xc8, 0x17, 0x08, 0x00, 0x01, 0xda, 0x7a, 0xc1, 0x51, 0x94, 0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf, 0xe7, 0x57, 0x00, 0x00, 0x52, 0x08, 0x97, 0xc0, 0x9f, 0xaf, 0x04, 0x00, 0xe7, 0x09, 0x94, 0x08, 0x90, 0x08, 0xe7, 0x57, 0xff, 0xff, 0x90, 0x08, 0x04, 0xc1, 0xe7, 0x07, 0xf0, 0x0c, 0x8e, 0x08, 0x97, 0xcf, 0xe7, 0x17, 0x32, 0x00, 0x90, 0x08, 0xe7, 0x67, 0xff, 0x07, 0x90, 0x08, 0xe7, 0x07, 0x26, 0x0d, 0x8e, 0x08, 0x97, 0xcf, 0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf, 0xe7, 0x57, 0x00, 0x00, 0x96, 0x08, 0x23, 0xc0, 0xe7, 0x07, 0x00, 0x80, 0x80, 0xc0, 0xe7, 0x07, 0x04, 0x00, 0x90, 0xc0, 0xe7, 0x07, 0x00, 0x00, 0x80, 0xc0, 0xe7, 0x07, 0x00, 0x80, 0x80, 0xc0, 0xc0, 0x07, 0x00, 0x00, 0xc0, 0x07, 0x00, 0x00, 0xc0, 0x07, 0x00, 0x00, 0xe7, 0x07, 0x00, 0x00, 0x80, 0xc0, 0xe7, 0x07, 0x00, 0x80, 0x80, 0xc0, 0xe7, 0x07, 0x00, 0x80, 0x40, 0xc0, 0xc0, 0x07, 0x00, 0x00, 0xe7, 0x07, 0x00, 0x00, 0x40, 0xc0, 0xe7, 0x07, 0x00, 0x00, 0x80, 0xc0, 0xef, 0x57, 0x00, 0x00, 0xf1, 0x09, 0x9f, 0xa0, 0xc0, 0x0d, 0xe7, 0x07, 0x04, 0x00, 0x90, 0xc0, 0xe7, 0x07, 0x00, 0x02, 0x40, 0xc0, 0xe7, 0x07, 0x0c, 0x02, 0x40, 0xc0, 0xe7, 0x07, 0x00, 0x00, 0x96, 0x08, 0xe7, 0x07, 0x00, 0x00, 0x8e, 0x08, 0xe7, 0x07, 0x00, 0x00, 0xaa, 0x08, 0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf, 0x9f, 0xaf, 0x9e, 0x03, 0xe7, 0x05, 0x00, 0xc0, 0x9f, 0xaf, 0xde, 0x01, 0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf, 0x9f, 0xaf, 0xde, 0x0d, 0xef, 0x77, 0x00, 0x00, 0xf1, 0x09, 0x97, 0xc1, 0x9f, 0xaf, 0xde, 0x0d, 0xef, 0x77, 0x00, 0x00, 0xf1, 0x09, 0x97, 0xc1, 0xef, 0x07, 0x01, 0x00, 0xf1, 0x09, 0xe7, 0x87, 0x00, 0x08, 0x1e, 0xc0, 0xe7, 0x87, 0x00, 0x08, 0x22, 0xc0, 0xe7, 0x67, 0xff, 0xf7, 0x22, 0xc0, 0xe7, 0x77, 0x00, 0x08, 0x20, 0xc0, 0x11, 0xc0, 0xe7, 0x67, 0xff, 0xf7, 0x1e, 0xc0, 0xe7, 0x87, 0x00, 0x08, 0x22, 0xc0, 0xe7, 0x67, 0xff, 0xf7, 0x22, 0xc0, 0xe7, 0x77, 0x00, 0x08, 0x20, 0xc0, 0x04, 0xc1, 0xe7, 0x87, 0x00, 0x08, 0x22, 0xc0, 0x97, 0xcf, 0xe7, 0x07, 0x01, 0x01, 0xf0, 0x09, 0xef, 0x57, 0x18, 0x00, 0xfe, 0xff, 0x97, 0xc2, 0xef, 0x07, 0x00, 0x00, 0xf0, 0x09, 0x97, 0xcf, 0xd7, 0x09, 0x00, 0xc0, 0x17, 0x00, 0x17, 0x02, 0x97, 0x02, 0xe7, 0x57, 0x00, 0x00, 0x7a, 0x08, 0x06, 0xc0, 0xc0, 0x09, 0x92, 0xc0, 0xc0, 0x77, 0x09, 0x02, 0x9f, 0xc1, 0xea, 0x06, 0x9f, 0xcf, 0x20, 0x08, 0xd7, 0x09, 0x0e, 0xc0, 0xe7, 0x07, 0x00, 0x00, 0x0e, 0xc0, 0x9f, 0xaf, 0x66, 0x0e, 0xe7, 0x05, 0x0e, 0xc0, 0x97, 0xcf, 0xd7, 0x09, 0x00, 0xc0, 0x17, 0x02, 0xc8, 0x09, 0xb0, 0xc0, 0xe7, 0x67, 0xfe, 0x7f, 0xb0, 0xc0, 0xc8, 0x77, 0x00, 0x20, 0x9f, 0xc1, 0x64, 0xeb, 0xe7, 0x57, 0x00, 0x00, 0xc8, 0x02, 0x9f, 0xc1, 0x80, 0xeb, 0xc8, 0x99, 0xca, 0x02, 0xc8, 0x67, 0x04, 0x00, 0x9f, 0xc1, 0x96, 0xeb, 0x9f, 0xcf, 0x4c, 0xeb, 0xe7, 0x07, 0x00, 0x00, 0xa6, 0xc0, 0xe7, 0x09, 0xb0, 0xc0, 0xc8, 0x02, 0xe7, 0x07, 0x03, 0x00, 0xb0, 0xc0, 0x97, 0xcf, 0xc0, 0x09, 0x86, 0x08, 0xc0, 0x37, 0x01, 0x00, 0x97, 0xc9, 0xc9, 0x09, 0x88, 0x08, 0x02, 0x00, 0x41, 0x90, 0x48, 0x02, 0xc9, 0x17, 0x06, 0x00, 0x9f, 0xaf, 0x64, 0x05, 0x9f, 0xa2, 0xd6, 0x0e, 0x02, 0xda, 0x77, 0xc1, 0x41, 0x60, 0x71, 0xc1, 0x97, 0xcf, 0x17, 0x02, 0x57, 0x02, 0x43, 0x04, 0x21, 0x04, 0xe0, 0x00, 0x43, 0x04, 0x21, 0x04, 0xe0, 0x00, 0x43, 0x04, 0x21, 0x04, 0xe0, 0x00, 0xc1, 0x07, 0x01, 0x00, 0xc9, 0x05, 0xc8, 0x05, 0x97, 0xcf, 0, 0 }; /* Firmware fixup (data?) segment */ Static unsigned char kue_fix_seg[] = { /******************************************/ /* NOTE: B6/C3 is data header signature */ /* 0xAA/0xBB is data length = total */ /* bytes - 7, 0xCC is type, 0xDD is */ /* interrupt to use. */ /******************************************/ 0xB6, 0xC3, 0xc9, 0x02, 0x03, 0x64, 0x02, 0x00, 0x08, 0x00, 0x24, 0x00, 0x2e, 0x00, 0x2c, 0x00, 0x3e, 0x00, 0x44, 0x00, 0x48, 0x00, 0x50, 0x00, 0x5c, 0x00, 0x60, 0x00, 0x66, 0x00, 0x6c, 0x00, 0x70, 0x00, 0x76, 0x00, 0x74, 0x00, 0x7a, 0x00, 0x7e, 0x00, 0x84, 0x00, 0x8a, 0x00, 0x8e, 0x00, 0x92, 0x00, 0x98, 0x00, 0x9c, 0x00, 0xa0, 0x00, 0xa8, 0x00, 0xae, 0x00, 0xb4, 0x00, 0xb2, 0x00, 0xba, 0x00, 0xbe, 0x00, 0xc4, 0x00, 0xc8, 0x00, 0xce, 0x00, 0xd2, 0x00, 0xd6, 0x00, 0xda, 0x00, 0xe2, 0x00, 0xe0, 0x00, 0xea, 0x00, 0xf2, 0x00, 0xfe, 0x00, 0x06, 0x01, 0x0c, 0x01, 0x1a, 0x01, 0x24, 0x01, 0x22, 0x01, 0x2a, 0x01, 0x30, 0x01, 0x36, 0x01, 0x3c, 0x01, 0x4e, 0x01, 0x52, 0x01, 0x58, 0x01, 0x5c, 0x01, 0x9c, 0x01, 0xb6, 0x01, 0xba, 0x01, 0xc0, 0x01, 0xca, 0x01, 0xd0, 0x01, 0xda, 0x01, 0xe2, 0x01, 0xea, 0x01, 0xf0, 0x01, 0x0a, 0x02, 0x0e, 0x02, 0x14, 0x02, 0x26, 0x02, 0x6c, 0x02, 0x8e, 0x02, 0x98, 0x02, 0xa0, 0x02, 0xa6, 0x02, 0xba, 0x02, 0xc6, 0x02, 0xce, 0x02, 0xe8, 0x02, 0xee, 0x02, 0xf4, 0x02, 0xf8, 0x02, 0x0a, 0x03, 0x10, 0x03, 0x1a, 0x03, 0x1e, 0x03, 0x2a, 0x03, 0x2e, 0x03, 0x34, 0x03, 0x3a, 0x03, 0x44, 0x03, 0x4e, 0x03, 0x5a, 0x03, 0x5e, 0x03, 0x6a, 0x03, 0x72, 0x03, 0x80, 0x03, 0x84, 0x03, 0x8c, 0x03, 0x94, 0x03, 0x98, 0x03, 0xa8, 0x03, 0xae, 0x03, 0xb4, 0x03, 0xba, 0x03, 0xce, 0x03, 0xcc, 0x03, 0xd6, 0x03, 0xdc, 0x03, 0xec, 0x03, 0xf0, 0x03, 0xfe, 0x03, 0x1c, 0x04, 0x30, 0x04, 0x38, 0x04, 0x3c, 0x04, 0x40, 0x04, 0x48, 0x04, 0x46, 0x04, 0x54, 0x04, 0x5e, 0x04, 0x64, 0x04, 0x74, 0x04, 0x78, 0x04, 0x84, 0x04, 0xd8, 0x04, 0xec, 0x04, 0xf0, 0x04, 0xf8, 0x04, 0xfe, 0x04, 0x1c, 0x05, 0x2c, 0x05, 0x30, 0x05, 0x4a, 0x05, 0x56, 0x05, 0x5a, 0x05, 0x88, 0x05, 0x8c, 0x05, 0x96, 0x05, 0x9a, 0x05, 0xa8, 0x05, 0xcc, 0x05, 0xd2, 0x05, 0xda, 0x05, 0xe0, 0x05, 0xe4, 0x05, 0xfc, 0x05, 0x06, 0x06, 0x14, 0x06, 0x12, 0x06, 0x1a, 0x06, 0x20, 0x06, 0x26, 0x06, 0x2e, 0x06, 0x34, 0x06, 0x48, 0x06, 0x52, 0x06, 0x64, 0x06, 0x86, 0x06, 0x90, 0x06, 0x9a, 0x06, 0xa0, 0x06, 0xac, 0x06, 0xaa, 0x06, 0xb2, 0x06, 0xb8, 0x06, 0xdc, 0x06, 0xda, 0x06, 0xe2, 0x06, 0xe8, 0x06, 0xf2, 0x06, 0xf8, 0x06, 0xfc, 0x06, 0x0a, 0x07, 0x10, 0x07, 0x14, 0x07, 0x24, 0x07, 0x2a, 0x07, 0x32, 0x07, 0x38, 0x07, 0xb2, 0x07, 0xba, 0x07, 0xde, 0x07, 0xe4, 0x07, 0x10, 0x08, 0x14, 0x08, 0x1a, 0x08, 0x1e, 0x08, 0x30, 0x08, 0x38, 0x08, 0x3c, 0x08, 0x44, 0x08, 0x42, 0x08, 0x48, 0x08, 0xc6, 0x08, 0xcc, 0x08, 0xd2, 0x08, 0xfe, 0x08, 0x04, 0x09, 0x0a, 0x09, 0x0e, 0x09, 0x12, 0x09, 0x16, 0x09, 0x20, 0x09, 0x24, 0x09, 0x28, 0x09, 0x32, 0x09, 0x46, 0x09, 0x4a, 0x09, 0x50, 0x09, 0x54, 0x09, 0x5a, 0x09, 0x60, 0x09, 0x7c, 0x09, 0x80, 0x09, 0xb8, 0x09, 0xbc, 0x09, 0xc0, 0x09, 0xc4, 0x09, 0xc8, 0x09, 0xcc, 0x09, 0xd0, 0x09, 0xd4, 0x09, 0xec, 0x09, 0xf4, 0x09, 0xf6, 0x09, 0xf8, 0x09, 0xfa, 0x09, 0xfc, 0x09, 0xfe, 0x09, 0x00, 0x0a, 0x02, 0x0a, 0x04, 0x0a, 0x06, 0x0a, 0x08, 0x0a, 0x0a, 0x0a, 0x0c, 0x0a, 0x10, 0x0a, 0x18, 0x0a, 0x24, 0x0a, 0x2c, 0x0a, 0x32, 0x0a, 0x3c, 0x0a, 0x46, 0x0a, 0x4c, 0x0a, 0x50, 0x0a, 0x54, 0x0a, 0x5a, 0x0a, 0x5e, 0x0a, 0x66, 0x0a, 0x6c, 0x0a, 0x72, 0x0a, 0x78, 0x0a, 0x7e, 0x0a, 0x7c, 0x0a, 0x82, 0x0a, 0x8c, 0x0a, 0x92, 0x0a, 0x90, 0x0a, 0x98, 0x0a, 0x96, 0x0a, 0xa2, 0x0a, 0xb2, 0x0a, 0xb6, 0x0a, 0xc4, 0x0a, 0xe2, 0x0a, 0xe0, 0x0a, 0xe8, 0x0a, 0xee, 0x0a, 0xf4, 0x0a, 0xf2, 0x0a, 0xf8, 0x0a, 0x0c, 0x0b, 0x1a, 0x0b, 0x24, 0x0b, 0x40, 0x0b, 0x44, 0x0b, 0x48, 0x0b, 0x4e, 0x0b, 0x4c, 0x0b, 0x52, 0x0b, 0x68, 0x0b, 0x6c, 0x0b, 0x70, 0x0b, 0x76, 0x0b, 0x88, 0x0b, 0x92, 0x0b, 0xbe, 0x0b, 0xca, 0x0b, 0xce, 0x0b, 0xde, 0x0b, 0xf4, 0x0b, 0xfa, 0x0b, 0x00, 0x0c, 0x24, 0x0c, 0x28, 0x0c, 0x30, 0x0c, 0x36, 0x0c, 0x3c, 0x0c, 0x40, 0x0c, 0x4a, 0x0c, 0x50, 0x0c, 0x58, 0x0c, 0x56, 0x0c, 0x5c, 0x0c, 0x60, 0x0c, 0x64, 0x0c, 0x80, 0x0c, 0x94, 0x0c, 0x9a, 0x0c, 0x98, 0x0c, 0x9e, 0x0c, 0xa4, 0x0c, 0xa2, 0x0c, 0xa8, 0x0c, 0xac, 0x0c, 0xb0, 0x0c, 0xb4, 0x0c, 0xb8, 0x0c, 0xbc, 0x0c, 0xce, 0x0c, 0xd2, 0x0c, 0xd6, 0x0c, 0xf4, 0x0c, 0xfa, 0x0c, 0x00, 0x0d, 0xfe, 0x0c, 0x06, 0x0d, 0x0e, 0x0d, 0x0c, 0x0d, 0x16, 0x0d, 0x1c, 0x0d, 0x22, 0x0d, 0x20, 0x0d, 0x30, 0x0d, 0x7e, 0x0d, 0x82, 0x0d, 0x9a, 0x0d, 0xa0, 0x0d, 0xa6, 0x0d, 0xb0, 0x0d, 0xb8, 0x0d, 0xc2, 0x0d, 0xc8, 0x0d, 0xce, 0x0d, 0xd4, 0x0d, 0xdc, 0x0d, 0x1e, 0x0e, 0x2c, 0x0e, 0x3e, 0x0e, 0x4c, 0x0e, 0x50, 0x0e, 0x5e, 0x0e, 0xae, 0x0e, 0xb8, 0x0e, 0xc6, 0x0e, 0xca, 0x0e, 0, 0 }; /* Fixup command. */ #define KUE_TRIGCMD_OFFSET 5 Static unsigned char kue_trig_seg[] = { 0xb6, 0xc3, 0x01, 0x00, 0x06, 0x64, 0x00, 0x00 }; pwcbsd/usb/if_kuereg.h000644 000423 000000 00000013101 10551670753 015505 0ustar00luigiwheel000000 000000 /*- * Copyright (c) 1997, 1998, 1999, 2000 * Bill Paul . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD: src/sys/dev/usb/if_kuereg.h,v 1.18 2005/06/10 16:49:15 brooks Exp $ */ /* * Definitions for the KLSI KL5KUSB101B USB to ethernet controller. * The KLSI part is controlled via vendor control requests, the structure * of which depend a bit on the firmware running on the internal * microcontroller. The one exception is the 'send scan data' command, * which is used to load the firmware. */ #define KUE_CMD_GET_ETHER_DESCRIPTOR 0x00 #define KUE_CMD_SET_MCAST_FILTERS 0x01 #define KUE_CMD_SET_PKT_FILTER 0x02 #define KUE_CMD_GET_ETHERSTATS 0x03 #define KUE_CMD_GET_GPIO 0x04 #define KUE_CMD_SET_GPIO 0x05 #define KUE_CMD_SET_MAC 0x06 #define KUE_CMD_GET_MAC 0x07 #define KUE_CMD_SET_URB_SIZE 0x08 #define KUE_CMD_SET_SOFS 0x09 #define KUE_CMD_SET_EVEN_PKTS 0x0A #define KUE_CMD_SEND_SCAN 0xFF struct kue_ether_desc { uint8_t kue_len; uint8_t kue_rsvd0; uint8_t kue_rsvd1; uint8_t kue_macaddr[ETHER_ADDR_LEN]; uint8_t kue_etherstats[4]; uint8_t kue_maxseg[2]; uint8_t kue_mcastfilt[2]; uint8_t kue_rsvd2; } __packed; #define KUE_ETHERSTATS(x) UGETDW((x)->sc_desc.kue_etherstats) #define KUE_MAXSEG(x) UGETW((x)->sc_desc.kue_maxseg) #define KUE_MCFILTCNT(x) (UGETW((x)->sc_desc.kue_mcastfilt) & 0x7FFF) #define KUE_MCFILT(x, y) \ ((x)->sc_mcfilters + ((y) * ETHER_ADDR_LEN)) #define KUE_MCFILT_MAX 64 #define KUE_STAT_TX_OK 0x00000001 #define KUE_STAT_RX_OK 0x00000002 #define KUE_STAT_TX_ERR 0x00000004 #define KUE_STAT_RX_ERR 0x00000008 #define KUE_STAT_RX_NOBUF 0x00000010 #define KUE_STAT_TX_UCAST_BYTES 0x00000020 #define KUE_STAT_TX_UCAST_FRAMES 0x00000040 #define KUE_STAT_TX_MCAST_BYTES 0x00000080 #define KUE_STAT_TX_MCAST_FRAMES 0x00000100 #define KUE_STAT_TX_BCAST_BYTES 0x00000200 #define KUE_STAT_TX_BCAST_FRAMES 0x00000400 #define KUE_STAT_RX_UCAST_BYTES 0x00000800 #define KUE_STAT_RX_UCAST_FRAMES 0x00001000 #define KUE_STAT_RX_MCAST_BYTES 0x00002000 #define KUE_STAT_RX_MCAST_FRAMES 0x00004000 #define KUE_STAT_RX_BCAST_BYTES 0x00008000 #define KUE_STAT_RX_BCAST_FRAMES 0x00010000 #define KUE_STAT_RX_CRCERR 0x00020000 #define KUE_STAT_TX_QUEUE_LENGTH 0x00040000 #define KUE_STAT_RX_ALIGNERR 0x00080000 #define KUE_STAT_TX_SINGLECOLL 0x00100000 #define KUE_STAT_TX_MULTICOLL 0x00200000 #define KUE_STAT_TX_DEFERRED 0x00400000 #define KUE_STAT_TX_MAXCOLLS 0x00800000 #define KUE_STAT_RX_OVERRUN 0x01000000 #define KUE_STAT_TX_UNDERRUN 0x02000000 #define KUE_STAT_TX_SQE_ERR 0x04000000 #define KUE_STAT_TX_CARRLOSS 0x08000000 #define KUE_STAT_RX_LATECOLL 0x10000000 #define KUE_RXFILT_PROMISC 0x0001 #define KUE_RXFILT_ALLMULTI 0x0002 #define KUE_RXFILT_UNICAST 0x0004 #define KUE_RXFILT_BROADCAST 0x0008 #define KUE_RXFILT_MULTICAST 0x0010 #define KUE_TIMEOUT 1000 #define KUE_MIN_FRAMELEN 60 #define KUE_CTL_READ 0x01 #define KUE_CTL_WRITE 0x02 #define KUE_CONFIG_NO 1 #define KUE_IFACE_IDX 0 /* The interrupt endpoint is currently unused by the KLSI part. */ #define KUE_ENDPT_MAX 4 struct kue_type { uint16_t kue_vid; uint16_t kue_did; }; struct kue_softc { struct usbd_config_td sc_config_td; struct __callout sc_watchdog; struct mtx sc_mtx; struct kue_ether_desc sc_desc; struct ifnet *sc_ifp; device_t sc_dev; struct usbd_device *sc_udev; struct usbd_xfer *sc_xfer[KUE_ENDPT_MAX]; uint32_t sc_unit; uint16_t sc_mcfilt_max; uint16_t sc_flags; #define KUE_FLAG_READ_STALL 0x0010 /* wait for clearing of stall */ #define KUE_FLAG_WRITE_STALL 0x0020 /* wait for clearing of stall */ #define KUE_FLAG_LL_READY 0x0040 /* Lower Layer Ready */ #define KUE_FLAG_HL_READY 0x0080 /* Higher Layer Ready */ #define KUE_FLAG_INTR_STALL 0x0100 /* wait for clearing of stall */ }; struct kue_config_copy { uint32_t if_flags; uint16_t if_rxfilt; uint16_t if_nhash; uint8_t if_lladdr[ETHER_ADDR_LEN]; uint8_t if_hash[KUE_MCFILT_MAX * ETHER_ADDR_LEN]; }; pwcbsd/usb/if_rue.c000644 000423 000000 00000102060 10551670753 015014 0ustar00luigiwheel000000 000000 /*- * Copyright (c) 2001-2003, Shunsuke Akiyama . * Copyright (c) 1997, 1998, 1999, 2000 Bill Paul . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /*- * Copyright (c) 1997, 1998, 1999, 2000 * Bill Paul . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD: src/sys/dev/usb/if_rue.c,v 1.30 2006/09/07 00:06:41 imp Exp $"); /* * RealTek RTL8150 USB to fast ethernet controller driver. * Datasheet is available from * ftp://ftp.realtek.com.tw/lancard/data_sheet/8150/. */ /* * NOTE: all function names beginning like "rue_cfg_" can only * be called from within the config thread function ! */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define usbd_config_td_cc rue_config_copy #define usbd_config_td_softc rue_softc #include #include #include #include "usbdevs.h" #include #include #include /* "device miibus" required. See GENERIC if you get errors here. */ #include "miibus_if.h" #ifdef USB_DEBUG #define DPRINTF(sc,n,fmt,...) \ do { if (rue_debug > (n)) { \ printf("%s:%s: " fmt, (sc)->sc_name, \ __FUNCTION__,## __VA_ARGS__); } } while (0) static int rue_debug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, rue, CTLFLAG_RW, 0, "USB rue"); SYSCTL_INT(_hw_usb_rue, OID_AUTO, debug, CTLFLAG_RW, &rue_debug, 0, "rue debug level"); #else #define DPRINTF(...) #endif /* * Various supported device vendors/products. */ static struct rue_type rue_devs[] = { { USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUAKTX }, { USB_VENDOR_REALTEK, USB_PRODUCT_REALTEK_USBKR100 }, { 0, 0 } }; /* prototypes */ static device_probe_t rue_probe; static device_attach_t rue_attach; static device_detach_t rue_detach; static device_shutdown_t rue_shutdown; static void rue_cfg_do_request(struct rue_softc *sc, usb_device_request_t *req, void *data); static void rue_cfg_read_mem(struct rue_softc *sc, u_int16_t addr, void *buf, u_int16_t len); static void rue_cfg_write_mem(struct rue_softc *sc, u_int16_t addr, void *buf, u_int16_t len); static u_int8_t rue_cfg_csr_read_1(struct rue_softc *sc, u_int16_t reg); static u_int16_t rue_cfg_csr_read_2(struct rue_softc *sc, u_int16_t reg); static void rue_cfg_csr_write_1(struct rue_softc *sc, u_int16_t reg, u_int8_t val); static void rue_cfg_csr_write_2(struct rue_softc *sc, u_int16_t reg, u_int16_t val); static void rue_cfg_csr_write_4(struct rue_softc *sc, int reg, u_int32_t val); static int rue_cfg_miibus_readreg(device_t dev, int phy, int reg); static int rue_cfg_miibus_writereg(device_t dev, int phy, int reg, int data); static void rue_cfg_miibus_statchg(device_t dev); static void rue_config_copy(struct rue_softc *sc, struct rue_config_copy *cc, u_int16_t refcount); static void rue_cfg_promisc_upd(struct rue_softc *sc, struct rue_config_copy *cc, u_int16_t refcount); static void rue_cfg_reset(struct rue_softc *sc); static void rue_cfg_first_time_setup(struct rue_softc *sc, struct rue_config_copy *cc, u_int16_t refcount); static void rue_intr_clear_stall_callback(struct usbd_xfer *xfer); static void rue_intr_callback(struct usbd_xfer *xfer); static void rue_bulk_read_clear_stall_callback(struct usbd_xfer *xfer); static void rue_bulk_read_callback(struct usbd_xfer *xfer); static void rue_bulk_write_clear_stall_callback(struct usbd_xfer *xfer); static void rue_bulk_write_callback(struct usbd_xfer *xfer); static void rue_cfg_tick(struct rue_softc *sc, struct rue_config_copy *cc, u_int16_t refcount); static void rue_start_cb(struct ifnet *ifp); static void rue_start_transfers(struct rue_softc *sc); static void rue_init_cb(void *arg); static void rue_cfg_init(struct rue_softc *sc, struct rue_config_copy *cc, u_int16_t refcount); static int rue_ifmedia_upd_cb(struct ifnet *ifp); static void rue_cfg_ifmedia_upd(struct rue_softc *sc, struct rue_config_copy *cc, u_int16_t refcount); static void rue_ifmedia_sts_cb(struct ifnet *ifp, struct ifmediareq *ifmr); static int rue_ioctl_cb(struct ifnet *ifp, u_long command, caddr_t data); static void rue_watchdog(void *arg); static void rue_cfg_stop(struct rue_softc *sc, struct rue_config_copy *cc, u_int16_t refcount); static const struct usbd_config rue_config[RUE_ENDPT_MAX] = { [0] = { .type = UE_BULK, .endpoint = -1, /* any */ .direction = UE_DIR_OUT, .bufsize = MCLBYTES, .flags = (USBD_USE_DMA|USBD_FORCE_SHORT_XFER), .callback = &rue_bulk_write_callback, .timeout = 10000, /* 10 seconds */ }, [1] = { .type = UE_BULK, .endpoint = -1, /* any */ .direction = UE_DIR_IN, .bufsize = (MCLBYTES + 4), .flags = (USBD_USE_DMA|USBD_SHORT_XFER_OK), .callback = &rue_bulk_read_callback, .timeout = 0, /* no timeout */ }, [2] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .flags = USBD_USE_DMA, .callback = &rue_bulk_write_clear_stall_callback, .timeout = 1000, /* 1 second */ }, [3] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .flags = USBD_USE_DMA, .callback = &rue_bulk_read_clear_stall_callback, .timeout = 1000, /* 1 second */ }, [4] = { .type = UE_INTERRUPT, .endpoint = -1, /* any */ .direction = UE_DIR_IN, .flags = USBD_SHORT_XFER_OK, .bufsize = 0, /* use wMaxPacketSize */ .callback = &rue_intr_callback, }, [5] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .flags = USBD_USE_DMA, .callback = &rue_intr_clear_stall_callback, .timeout = 1000, /* 1 second */ }, }; static device_method_t rue_methods[] = { /* Device interface */ DEVMETHOD(device_probe, rue_probe), DEVMETHOD(device_attach, rue_attach), DEVMETHOD(device_detach, rue_detach), DEVMETHOD(device_shutdown, rue_shutdown), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_driver_added, bus_generic_driver_added), /* MII interface */ DEVMETHOD(miibus_readreg, rue_cfg_miibus_readreg), DEVMETHOD(miibus_writereg, rue_cfg_miibus_writereg), DEVMETHOD(miibus_statchg, rue_cfg_miibus_statchg), { 0, 0 } }; static driver_t rue_driver = { .name = "rue", .methods = rue_methods, .size = sizeof(struct rue_softc), }; static devclass_t rue_devclass; DRIVER_MODULE(rue, uhub, rue_driver, rue_devclass, usbd_driver_load, 0); DRIVER_MODULE(miibus, rue, miibus_driver, miibus_devclass, 0, 0); MODULE_DEPEND(rue, usb, 1, 1, 1); MODULE_DEPEND(rue, ether, 1, 1, 1); MODULE_DEPEND(rue, miibus, 1, 1, 1); static void rue_cfg_do_request(struct rue_softc *sc, usb_device_request_t *req, void *data) { u_int16_t length; usbd_status err; if (usbd_config_td_is_gone(&(sc->sc_config_td))) { goto error; } err = usbd_do_request_flags_mtx(sc->sc_udev, &(sc->sc_mtx), req, data, 0, NULL, 1000); if (err) { DPRINTF(sc, 0, "device request failed, err=%s " "(ignored)\n", usbd_errstr(err)); error: length = UGETW(req->wLength); if ((req->bmRequestType & UT_READ) && length) { bzero(data, length); } } return; } #define RUE_CFG_SETBIT(sc, reg, x) \ rue_cfg_csr_write_1(sc, reg, rue_cfg_csr_read_1(sc, reg) | (x)) #define RUE_CFG_CLRBIT(sc, reg, x) \ rue_cfg_csr_write_1(sc, reg, rue_cfg_csr_read_1(sc, reg) & ~(x)) static void rue_cfg_read_mem(struct rue_softc *sc, u_int16_t addr, void *buf, u_int16_t len) { usb_device_request_t req; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = UR_SET_ADDRESS; USETW(req.wValue, addr); USETW(req.wIndex, 0); USETW(req.wLength, len); rue_cfg_do_request(sc, &req, buf); return; } static void rue_cfg_write_mem(struct rue_softc *sc, u_int16_t addr, void *buf, u_int16_t len) { usb_device_request_t req; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = UR_SET_ADDRESS; USETW(req.wValue, addr); USETW(req.wIndex, 0); USETW(req.wLength, len); rue_cfg_do_request(sc, &req, buf); return; } static u_int8_t rue_cfg_csr_read_1(struct rue_softc *sc, u_int16_t reg) { u_int8_t val; rue_cfg_read_mem(sc, reg, &val, 1); return val; } static u_int16_t rue_cfg_csr_read_2(struct rue_softc *sc, u_int16_t reg) { u_int8_t val[2]; rue_cfg_read_mem(sc, reg, &val, 2); return UGETW(val); } static void rue_cfg_csr_write_1(struct rue_softc *sc, u_int16_t reg, u_int8_t val) { rue_cfg_write_mem(sc, reg, &val, 1); return; } static void rue_cfg_csr_write_2(struct rue_softc *sc, u_int16_t reg, u_int16_t val) { u_int8_t temp[2]; USETW(temp, val); rue_cfg_write_mem(sc, reg, &temp, 2); return; } static void rue_cfg_csr_write_4(struct rue_softc *sc, int reg, u_int32_t val) { u_int8_t temp[4]; USETDW(temp, val); rue_cfg_write_mem(sc, reg, &temp, 4); return; } static int rue_cfg_miibus_readreg(device_t dev, int phy, int reg) { struct rue_softc * sc = device_get_softc(dev); u_int16_t rval; u_int16_t ruereg; if (phy != 0) { /* RTL8150 supports PHY == 0, only */ return 0; } mtx_lock(&(sc->sc_mtx)); /* XXX */ switch (reg) { case MII_BMCR: ruereg = RUE_BMCR; break; case MII_BMSR: ruereg = RUE_BMSR; break; case MII_ANAR: ruereg = RUE_ANAR; break; case MII_ANER: ruereg = RUE_AER; break; case MII_ANLPAR: ruereg = RUE_ANLP; break; case MII_PHYIDR1: case MII_PHYIDR2: rval = 0; goto done; default: if ((RUE_REG_MIN <= reg) && (reg <= RUE_REG_MAX)) { rval = rue_cfg_csr_read_1(sc, reg); goto done; } printf("rue%d: bad phy register\n", sc->sc_unit); rval = 0; goto done; } rval = rue_cfg_csr_read_2(sc, ruereg); done: mtx_unlock(&(sc->sc_mtx)); /* XXX */ return rval; } static int rue_cfg_miibus_writereg(device_t dev, int phy, int reg, int data) { struct rue_softc * sc = device_get_softc(dev); u_int16_t ruereg; if (phy != 0) { /* RTL8150 supports PHY == 0, only */ return 0; } mtx_lock(&(sc->sc_mtx)); /* XXX */ switch (reg) { case MII_BMCR: ruereg = RUE_BMCR; break; case MII_BMSR: ruereg = RUE_BMSR; break; case MII_ANAR: ruereg = RUE_ANAR; break; case MII_ANER: ruereg = RUE_AER; break; case MII_ANLPAR: ruereg = RUE_ANLP; break; case MII_PHYIDR1: case MII_PHYIDR2: goto done; default: if ((RUE_REG_MIN <= reg) && (reg <= RUE_REG_MAX)) { rue_cfg_csr_write_1(sc, reg, data); goto done; } printf("%s: bad phy register\n", sc->sc_name); goto done; } rue_cfg_csr_write_2(sc, ruereg, data); done: mtx_unlock(&(sc->sc_mtx)); /* XXX */ return 0; } static void rue_cfg_miibus_statchg(device_t dev) { /* * When the code below is enabled the card starts doing weird * things after link going from UP to DOWN and back UP. * * Looks like some of register writes below messes up PHY * interface. * * No visible regressions were found after commenting this code * out, so that disable it for good. */ #if 0 struct rue_softc * sc = device_get_softc(dev); struct mii_data * mii = GET_MII(sc); u_int16_t bmcr; mtx_lock(&(sc->sc_mtx)); /* XXX */ RUE_CFG_CLRBIT(sc, RUE_CR, (RUE_CR_RE | RUE_CR_TE)); bmcr = rue_cfg_csr_read_2(sc, RUE_BMCR); if (IFM_SUBTYPE(mii->mii_media_active) == IFM_100_TX) bmcr |= RUE_BMCR_SPD_SET; else bmcr &= ~RUE_BMCR_SPD_SET; if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) bmcr |= RUE_BMCR_DUPLEX; else bmcr &= ~RUE_BMCR_DUPLEX; rue_cfg_csr_write_2(sc, RUE_BMCR, bmcr); RUE_CFG_SETBIT(sc, RUE_CR, (RUE_CR_RE | RUE_CR_TE)); mtx_unlock(&(sc->sc_mtx)); /* XXX */ #endif return; } static void rue_config_copy(struct rue_softc *sc, struct rue_config_copy *cc, u_int16_t refcount) { struct ifnet * ifp = sc->sc_ifp; struct ifmultiaddr * ifma; u_int8_t h; u_int8_t i; bzero(cc, sizeof(*cc)); if (ifp) { for (i = 0; i < ETHER_ADDR_LEN; i++) { cc->if_lladdr[i] = IF_LLADDR(ifp)[i]; } cc->if_flags = ifp->if_flags; /* compute hash bits for multicast filter */ IF_ADDR_LOCK(ifp); TAILQ_FOREACH (ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) { continue; } h = ether_crc32_be(LLADDR((struct sockaddr_dl *) ifma->ifma_addr), ETHER_ADDR_LEN) >> 26; if (h < 32) cc->if_hashes[0] |= (1 << h); else cc->if_hashes[1] |= (1 << (h - 32)); } IF_ADDR_UNLOCK(ifp); } return; } /* * Program the 64-bit multicast hash filter. */ static void rue_cfg_promisc_upd(struct rue_softc *sc, struct rue_config_copy *cc, u_int16_t refcount) { u_int16_t rxcfg; if (cc == NULL) { /* nothing to do */ return; } rxcfg = rue_cfg_csr_read_2(sc, RUE_RCR); if ((cc->if_flags & IFF_ALLMULTI) || (cc->if_flags & IFF_PROMISC)) { rxcfg |= (RUE_RCR_AAM | RUE_RCR_AAP); rxcfg &= ~RUE_RCR_AM; rue_cfg_csr_write_2(sc, RUE_RCR, rxcfg); rue_cfg_csr_write_4(sc, RUE_MAR0, 0xFFFFFFFF); rue_cfg_csr_write_4(sc, RUE_MAR4, 0xFFFFFFFF); return; } /* first, zero all the existing hash bits */ rue_cfg_csr_write_4(sc, RUE_MAR0, 0); rue_cfg_csr_write_4(sc, RUE_MAR4, 0); if (cc->if_hashes[0] || cc->if_hashes[1]) rxcfg |= RUE_RCR_AM; else rxcfg &= ~RUE_RCR_AM; rxcfg &= ~(RUE_RCR_AAM | RUE_RCR_AAP); rue_cfg_csr_write_2(sc, RUE_RCR, rxcfg); rue_cfg_csr_write_4(sc, RUE_MAR0, cc->if_hashes[0]); rue_cfg_csr_write_4(sc, RUE_MAR4, cc->if_hashes[1]); return; } static void rue_cfg_reset(struct rue_softc *sc) { usbd_status err; u_int16_t to; rue_cfg_csr_write_1(sc, RUE_CR, RUE_CR_SOFT_RST); for (to = 0; ; to++) { if (to < RUE_TIMEOUT) { err = usbd_config_td_sleep(&(sc->sc_config_td), hz/100); if (err) { break; } if (!(rue_cfg_csr_read_1(sc, RUE_CR) & RUE_CR_SOFT_RST)) { break; } } else { printf("%s: reset timeout!\n", sc->sc_name); break; } } err = usbd_config_td_sleep(&(sc->sc_config_td), hz/100); return; } /* * Probe for a RTL8150 chip. */ static int rue_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct rue_type *t; if (uaa->iface != NULL) { return UMATCH_NONE; } t = rue_devs; while (t->rue_vid) { if ((uaa->vendor == t->rue_vid) && (uaa->product == t->rue_did)) { return UMATCH_VENDOR_PRODUCT; } t++; } return UMATCH_NONE; } /* * Attach the interface. Allocate softc structures, do ifmedia * setup and ethernet/BPF attach. */ static int rue_attach(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct rue_softc *sc = device_get_softc(dev); int32_t error; if (sc == NULL) { return ENOMEM; } sc->sc_udev = uaa->device; sc->sc_dev = dev; sc->sc_unit = device_get_unit(dev); usbd_set_desc(dev, uaa->device); snprintf(sc->sc_name, sizeof(sc->sc_name), "%s", device_get_nameunit(dev)); mtx_init(&(sc->sc_mtx), "rue lock", NULL, MTX_DEF | MTX_RECURSE); __callout_init_mtx(&(sc->sc_watchdog), &(sc->sc_mtx), CALLOUT_RETURNUNLOCKED); error = usbd_set_config_no(uaa->device, RUE_CONFIG_NO, 0); if (error) { device_printf(dev, "setting config " "number failed!\n"); goto detach; } error = usbd_transfer_setup(uaa->device, RUE_IFACE_IDX, sc->sc_xfer, rue_config, RUE_ENDPT_MAX, sc, &(sc->sc_mtx)); if (error) { device_printf(dev, "allocating USB " "transfers failed!\n"); goto detach; } error = usbd_config_td_setup(&(sc->sc_config_td), sc, &(sc->sc_mtx), &rue_config_copy, NULL, sizeof(struct rue_config_copy), 16); if (error) { device_printf(dev, "could not setup config " "thread!\n"); goto detach; } mtx_lock(&(sc->sc_mtx)); sc->sc_flags |= RUE_FLAG_WAIT_LINK; /* start setup */ usbd_config_td_queue_command (&(sc->sc_config_td), &rue_cfg_first_time_setup, 0); /* start watchdog (will exit mutex) */ rue_watchdog(sc); return 0; /* success */ detach: rue_detach(dev); return ENXIO; /* failure */ } static void rue_cfg_first_time_setup(struct rue_softc *sc, struct rue_config_copy *cc, u_int16_t refcount) { struct ifnet * ifp; int error; u_int8_t eaddr[min(ETHER_ADDR_LEN,6)]; if (cc == NULL) { return; } /* reset the adapter */ rue_cfg_reset(sc); /* get station address from the EEPROM */ rue_cfg_read_mem(sc, RUE_EEPROM_IDR0, eaddr, ETHER_ADDR_LEN); mtx_unlock(&(sc->sc_mtx)); ifp = if_alloc(IFT_ETHER); mtx_lock(&(sc->sc_mtx)); if (ifp == NULL) { printf("%s: could not if_alloc()\n", sc->sc_name); goto done; } ifp->if_softc = sc; if_initname(ifp, "rue", sc->sc_unit); ifp->if_mtu = ETHERMTU; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = rue_ioctl_cb; ifp->if_start = rue_start_cb; ifp->if_watchdog = NULL; ifp->if_init = rue_init_cb; ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; /* XXX need Giant when accessing * the device structures ! */ mtx_unlock(&(sc->sc_mtx)); mtx_lock(&Giant); /* MII setup */ error = mii_phy_probe(sc->sc_dev, &(sc->sc_miibus), &rue_ifmedia_upd_cb, &rue_ifmedia_sts_cb); mtx_unlock(&Giant); mtx_lock(&(sc->sc_mtx)); if (error) { printf("%s: MII without any PHY!\n", sc->sc_name); if_free(ifp); goto done; } sc->sc_ifp = ifp; /* * Call MI attach routine. */ ether_ifattach(ifp, eaddr); done: return; } static int rue_detach(device_t dev) { struct rue_softc * sc = device_get_softc(dev); struct ifnet * ifp; mtx_lock(&(sc->sc_mtx)); usbd_config_td_stop(&(sc->sc_config_td)); __callout_stop(&sc->sc_watchdog); rue_cfg_stop(sc, NULL, 0); ifp = sc->sc_ifp; mtx_unlock(&(sc->sc_mtx)); /* get rid of any late children */ bus_generic_detach(dev); if (ifp) { ether_ifdetach(ifp); if_free(ifp); } usbd_transfer_unsetup(sc->sc_xfer, RUE_ENDPT_MAX); usbd_config_td_unsetup(&(sc->sc_config_td)); __callout_drain(&(sc->sc_watchdog)); mtx_destroy(&(sc->sc_mtx)); return 0; } static void rue_intr_clear_stall_callback(struct usbd_xfer *xfer) { struct rue_softc *sc = xfer->priv_sc; struct usbd_xfer *xfer_other = sc->sc_xfer[4]; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, xfer_other); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, xfer_other); sc->sc_flags &= ~RUE_FLAG_INTR_STALL; usbd_transfer_start(xfer_other); return; tr_error: /* bomb out */ sc->sc_flags &= ~RUE_FLAG_INTR_STALL; DPRINTF(sc, 0, "interrupt read pipe stopped\n"); return; } static void rue_intr_callback(struct usbd_xfer *xfer) { struct rue_softc *sc = xfer->priv_sc; struct ifnet *ifp = sc->sc_ifp; struct rue_intrpkt *p = xfer->buffer; USBD_CHECK_STATUS(xfer); tr_transferred: if (ifp && (ifp->if_drv_flags & IFF_DRV_RUNNING) && (xfer->actlen >= sizeof(*p))) { ifp->if_ierrors += p->rue_rxlost_cnt; ifp->if_ierrors += p->rue_crcerr_cnt; ifp->if_collisions += p->rue_col_cnt; } tr_setup: if (sc->sc_flags & RUE_FLAG_INTR_STALL) { usbd_transfer_start(sc->sc_xfer[5]); } else { usbd_start_hardware(xfer); } return; tr_error: if (xfer->error != USBD_CANCELLED) { /* start clear stall */ sc->sc_flags |= RUE_FLAG_INTR_STALL; usbd_transfer_start(sc->sc_xfer[5]); } return; } static void rue_bulk_read_clear_stall_callback(struct usbd_xfer *xfer) { struct rue_softc *sc = xfer->priv_sc; struct usbd_xfer *xfer_other = sc->sc_xfer[1]; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, xfer_other); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, xfer_other); sc->sc_flags &= ~RUE_FLAG_READ_STALL; usbd_transfer_start(xfer_other); return; tr_error: /* bomb out */ sc->sc_flags &= ~RUE_FLAG_READ_STALL; DPRINTF(sc, 0, "bulk read pipe stopped\n"); return; } static void rue_bulk_read_callback(struct usbd_xfer *xfer) { struct rue_softc *sc = xfer->priv_sc; struct ifnet *ifp = sc->sc_ifp; u_int16_t status; struct mbuf *m; USBD_CHECK_STATUS(xfer); tr_error: if (xfer->error != USBD_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= RUE_FLAG_READ_STALL; usbd_transfer_start(sc->sc_xfer[3]); } DPRINTF(sc, 0, "bulk read error, %s\n", usbd_errstr(xfer->error)); return; tr_transferred: if (xfer->actlen < 4) { ifp->if_ierrors++; goto tr_setup; } usbd_copy_out(&(xfer->buf_data), xfer->actlen - 4, &status, sizeof(status)); status = le16toh(status); /* check recieve packet was valid or not */ if ((status & RUE_RXSTAT_VALID) == 0) { ifp->if_ierrors++; goto tr_setup; } xfer->actlen -= 4; if (xfer->actlen < sizeof(struct ether_header)) { ifp->if_ierrors++; goto tr_setup; } m = usbd_ether_get_mbuf(); if (m == NULL) { ifp->if_ierrors++; goto tr_setup; } xfer->actlen = min(xfer->actlen, m->m_len); usbd_copy_out(&(xfer->buf_data), 0, m->m_data, xfer->actlen); ifp->if_ipackets++; m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = m->m_len = xfer->actlen; (ifp->if_input)(ifp, m); tr_setup: if (sc->sc_flags & RUE_FLAG_READ_STALL) { usbd_transfer_start(sc->sc_xfer[3]); } else { usbd_start_hardware(xfer); } return; } static void rue_bulk_write_clear_stall_callback(struct usbd_xfer *xfer) { struct rue_softc *sc = xfer->priv_sc; struct usbd_xfer *xfer_other = sc->sc_xfer[0]; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, xfer_other); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, xfer_other); sc->sc_flags &= ~RUE_FLAG_WRITE_STALL; usbd_transfer_start(xfer_other); return; tr_error: /* bomb out */ sc->sc_flags &= ~RUE_FLAG_WRITE_STALL; DPRINTF(sc, 0, "bulk write pipe stopped\n"); return; } static void rue_bulk_write_callback(struct usbd_xfer *xfer) { struct rue_softc *sc = xfer->priv_sc; struct ifnet *ifp = sc->sc_ifp; struct mbuf *m; USBD_CHECK_STATUS(xfer); tr_error: DPRINTF(sc, 10, "transfer error, %s\n", usbd_errstr(xfer->error)); if (xfer->error != USBD_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= RUE_FLAG_WRITE_STALL; usbd_transfer_start(sc->sc_xfer[2]); } ifp->if_oerrors++; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; return; tr_transferred: DPRINTF(sc, 10, "transfer complete\n"); ifp->if_opackets++; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; tr_setup: if (sc->sc_flags & RUE_FLAG_WRITE_STALL) { usbd_transfer_start(sc->sc_xfer[2]); goto done; } if (sc->sc_flags & RUE_FLAG_WAIT_LINK) { /* don't send anything * if there is no link ! */ goto done; } IF_DEQUEUE(&(ifp->if_snd), m); if (m == NULL) { goto done; } if (m->m_pkthdr.len > MCLBYTES) { m->m_pkthdr.len = MCLBYTES; } xfer->length = m->m_pkthdr.len; usbd_m_copy_in(&(xfer->buf_data), 0, m, 0, m->m_pkthdr.len); /* * This is an undocumented behavior. * RTL8150 chip doesn't send frame length smaller than * RUE_MIN_FRAMELEN (60) byte packet. */ if (xfer->length < RUE_MIN_FRAMELEN) { usbd_bzero(&(xfer->buf_data), xfer->length, RUE_MIN_FRAMELEN - xfer->length); xfer->length = RUE_MIN_FRAMELEN; } /* * if there's a BPF listener, bounce a copy * of this frame to him: */ BPF_MTAP(ifp, m); m_freem(m); usbd_start_hardware(xfer); ifp->if_drv_flags |= IFF_DRV_OACTIVE; done: return; } static void rue_cfg_tick(struct rue_softc *sc, struct rue_config_copy *cc, u_int16_t refcount) { struct ifnet * ifp = sc->sc_ifp; struct mii_data * mii = GET_MII(sc); if ((cc == NULL) || (ifp == NULL) || (mii == NULL)) { /* not ready */ return; } mii_tick(mii); mii_pollstat(mii); if ((sc->sc_flags & RUE_FLAG_WAIT_LINK) && (mii->mii_media_status & IFM_ACTIVE) && (IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE)) { sc->sc_flags &= ~RUE_FLAG_WAIT_LINK; } sc->sc_media_active = mii->mii_media_active; sc->sc_media_status = mii->mii_media_status; /* start stopped transfers, if any */ rue_start_transfers(sc); return; } static void rue_start_cb(struct ifnet *ifp) { struct rue_softc *sc = ifp->if_softc; mtx_lock(&(sc->sc_mtx)); rue_start_transfers(sc); mtx_unlock(&(sc->sc_mtx)); return; } static void rue_start_transfers(struct rue_softc *sc) { if ((sc->sc_flags & RUE_FLAG_LL_READY) && (sc->sc_flags & RUE_FLAG_HL_READY)) { /* start the USB transfers, * if not already started: */ usbd_transfer_start(sc->sc_xfer[4]); usbd_transfer_start(sc->sc_xfer[1]); usbd_transfer_start(sc->sc_xfer[0]); } return; } static void rue_init_cb(void *arg) { struct rue_softc *sc = arg; mtx_lock(&(sc->sc_mtx)); usbd_config_td_queue_command (&(sc->sc_config_td), &rue_cfg_init, 0); mtx_unlock(&(sc->sc_mtx)); return; } static void rue_cfg_init(struct rue_softc *sc, struct rue_config_copy *cc, u_int16_t refcount) { struct mii_data *mii = GET_MII(sc); u_int16_t rxcfg; if (cc == NULL) { /* immediate configuration */ struct ifnet *ifp = sc->sc_ifp; rue_cfg_stop(sc, NULL, 0); ifp->if_drv_flags |= IFF_DRV_RUNNING; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; sc->sc_flags |= RUE_FLAG_HL_READY; return; } /* * Cancel pending I/O */ rue_cfg_stop(sc, cc, 0); /* set MAC address */ rue_cfg_write_mem(sc, RUE_IDR0, cc->if_lladdr, ETHER_ADDR_LEN); /* * Set the initial TX and RX configuration. */ rue_cfg_csr_write_1(sc, RUE_TCR, RUE_TCR_CONFIG); rxcfg = RUE_RCR_CONFIG; /* Set capture broadcast bit to capture broadcast frames. */ if (cc->if_flags & IFF_BROADCAST) rxcfg |= RUE_RCR_AB; else rxcfg &= ~RUE_RCR_AB; rue_cfg_csr_write_2(sc, RUE_RCR, rxcfg); /* Load the multicast filter */ rue_cfg_promisc_upd(sc, cc, 0); /* Enable RX and TX */ rue_cfg_csr_write_1(sc, RUE_CR, (RUE_CR_TE | RUE_CR_RE | RUE_CR_EP3CLREN)); mii_mediachg(mii); sc->sc_flags |= (RUE_FLAG_READ_STALL| RUE_FLAG_WRITE_STALL| RUE_FLAG_LL_READY); rue_start_transfers(sc); return; } /* * Set media options. */ static int rue_ifmedia_upd_cb(struct ifnet *ifp) { struct rue_softc *sc = ifp->if_softc; mtx_lock(&(sc->sc_mtx)); usbd_config_td_queue_command (&(sc->sc_config_td), &rue_cfg_ifmedia_upd, 0); mtx_unlock(&(sc->sc_mtx)); return 0; } static void rue_cfg_ifmedia_upd(struct rue_softc *sc, struct rue_config_copy *cc, u_int16_t refcount) { struct ifnet * ifp = sc->sc_ifp; struct mii_data * mii = GET_MII(sc); if ((cc == NULL) || (ifp == NULL) || (mii == NULL)) { /* not ready */ return; } sc->sc_flags |= RUE_FLAG_WAIT_LINK; if (mii->mii_instance) { struct mii_softc *miisc; LIST_FOREACH(miisc, &mii->mii_phys, mii_list) { mii_phy_reset(miisc); } } mii_mediachg(mii); return; } /* * Report current media status. */ static void rue_ifmedia_sts_cb(struct ifnet *ifp, struct ifmediareq *ifmr) { struct rue_softc *sc = ifp->if_softc; mtx_lock(&(sc->sc_mtx)); ifmr->ifm_active = sc->sc_media_active; ifmr->ifm_status = sc->sc_media_status; mtx_unlock(&(sc->sc_mtx)); return; } static int rue_ioctl_cb(struct ifnet *ifp, u_long command, caddr_t data) { struct rue_softc * sc = ifp->if_softc; struct mii_data * mii; int error = 0; mtx_lock(&(sc->sc_mtx)); switch (command) { case SIOCSIFFLAGS: if (ifp->if_flags & IFF_UP) { if (ifp->if_drv_flags & IFF_DRV_RUNNING) { usbd_config_td_queue_command (&(sc->sc_config_td), &rue_cfg_promisc_upd, 0); } else { usbd_config_td_queue_command (&(sc->sc_config_td), &rue_cfg_init, 0); } } else { if (ifp->if_drv_flags & IFF_DRV_RUNNING) { usbd_config_td_queue_command (&(sc->sc_config_td), &rue_cfg_stop, 0); } } break; case SIOCADDMULTI: case SIOCDELMULTI: usbd_config_td_queue_command (&(sc->sc_config_td), &rue_cfg_promisc_upd, 0); break; case SIOCGIFMEDIA: case SIOCSIFMEDIA: mii = GET_MII(sc); if (mii == NULL) { error = EINVAL; } else { error = ifmedia_ioctl (ifp, (void *)data, &(mii->mii_media), command); } break; default: error = ether_ioctl(ifp, command, data); break; } mtx_unlock(&(sc->sc_mtx)); return error; } static void rue_watchdog(void *arg) { struct rue_softc *sc = arg; mtx_assert(&(sc->sc_mtx), MA_OWNED); usbd_config_td_queue_command (&(sc->sc_config_td), &rue_cfg_tick, 0); __callout_reset(&(sc->sc_watchdog), hz, &rue_watchdog, sc); mtx_unlock(&(sc->sc_mtx)); return; } /* * NOTE: can be called when "ifp" is NULL */ static void rue_cfg_stop(struct rue_softc *sc, struct rue_config_copy *cc, u_int16_t refcount) { if (cc == NULL) { /* immediate configuration */ struct ifnet *ifp = sc->sc_ifp; if (ifp) { /* clear flags */ ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); } sc->sc_flags &= ~(RUE_FLAG_HL_READY| RUE_FLAG_LL_READY); sc->sc_flags |= RUE_FLAG_WAIT_LINK; /* stop all the transfers, * if not already stopped: */ if (sc->sc_xfer[0]) { usbd_transfer_stop(sc->sc_xfer[0]); } if (sc->sc_xfer[1]) { usbd_transfer_stop(sc->sc_xfer[1]); } if (sc->sc_xfer[2]) { usbd_transfer_stop(sc->sc_xfer[2]); } if (sc->sc_xfer[3]) { usbd_transfer_stop(sc->sc_xfer[3]); } if (sc->sc_xfer[4]) { usbd_transfer_stop(sc->sc_xfer[4]); } if (sc->sc_xfer[5]) { usbd_transfer_stop(sc->sc_xfer[5]); } return; } rue_cfg_csr_write_1(sc, RUE_CR, 0x00); rue_cfg_reset(sc); return; } /* * Stop all chip I/O so that the kernel's probe routines don't * get confused by errant DMAs when rebooting. */ static int rue_shutdown(device_t dev) { struct rue_softc * sc = device_get_softc(dev); mtx_lock(&(sc->sc_mtx)); usbd_config_td_queue_command (&(sc->sc_config_td), &rue_cfg_stop, 0); mtx_unlock(&(sc->sc_mtx)); return 0; } pwcbsd/usb/if_ruereg.h000644 000423 000000 00000012476 10551670753 015532 0ustar00luigiwheel000000 000000 /*- * Copyright (c) 2001-2003, Shunsuke Akiyama . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD: src/sys/dev/usb/if_ruereg.h,v 1.6 2005/06/10 16:49:15 brooks Exp $ */ #define RUE_CONFIG_NO 1 #define RUE_IFACE_IDX 0 #define RUE_ENDPT_MAX 6 #define RUE_INTR_PKTLEN 0x8 #define RUE_TIMEOUT 50 #define RUE_MIN_FRAMELEN 60 /* Registers. */ #define RUE_IDR0 0x0120 #define RUE_IDR1 0x0121 #define RUE_IDR2 0x0122 #define RUE_IDR3 0x0123 #define RUE_IDR4 0x0124 #define RUE_IDR5 0x0125 #define RUE_MAR0 0x0126 #define RUE_MAR1 0x0127 #define RUE_MAR2 0x0128 #define RUE_MAR3 0x0129 #define RUE_MAR4 0x012A #define RUE_MAR5 0x012B #define RUE_MAR6 0x012C #define RUE_MAR7 0x012D #define RUE_CR 0x012E /* B, R/W */ #define RUE_CR_SOFT_RST 0x10 #define RUE_CR_RE 0x08 #define RUE_CR_TE 0x04 #define RUE_CR_EP3CLREN 0x02 #define RUE_TCR 0x012F /* B, R/W */ #define RUE_TCR_TXRR1 0x80 #define RUE_TCR_TXRR0 0x40 #define RUE_TCR_IFG1 0x10 #define RUE_TCR_IFG0 0x08 #define RUE_TCR_NOCRC 0x01 #define RUE_TCR_CONFIG (RUE_TCR_TXRR1 | RUE_TCR_TXRR0 | \ RUE_TCR_IFG1 | RUE_TCR_IFG0) #define RUE_RCR 0x0130 /* W, R/W */ #define RUE_RCR_TAIL 0x80 #define RUE_RCR_AER 0x40 #define RUE_RCR_AR 0x20 #define RUE_RCR_AM 0x10 #define RUE_RCR_AB 0x08 #define RUE_RCR_AD 0x04 #define RUE_RCR_AAM 0x02 #define RUE_RCR_AAP 0x01 #define RUE_RCR_CONFIG (RUE_RCR_TAIL | RUE_RCR_AD) #define RUE_TSR 0x0132 #define RUE_RSR 0x0133 #define RUE_CON0 0x0135 #define RUE_CON1 0x0136 #define RUE_MSR 0x0137 #define RUE_PHYADD 0x0138 #define RUE_PHYDAT 0x0139 #define RUE_PHYCNT 0x013B /* B, R/W */ #define RUE_PHYCNT_PHYOWN 0x40 #define RUE_PHYCNT_RWCR 0x20 #define RUE_GPPC 0x013D #define RUE_WAKECNT 0x013E #define RUE_BMCR 0x0140 #define RUE_BMCR_SPD_SET 0x2000 #define RUE_BMCR_DUPLEX 0x0100 #define RUE_BMSR 0x0142 #define RUE_ANAR 0x0144 /* W, R/W */ #define RUE_ANAR_PAUSE 0x0400 #define RUE_ANLP 0x0146 /* W, R/O */ #define RUE_ANLP_PAUSE 0x0400 #define RUE_AER 0x0148 #define RUE_NWAYT 0x014A #define RUE_CSCR 0x014C #define RUE_CRC0 0x014E #define RUE_CRC1 0x0150 #define RUE_CRC2 0x0152 #define RUE_CRC3 0x0154 #define RUE_CRC4 0x0156 #define RUE_BYTEMASK0 0x0158 #define RUE_BYTEMASK1 0x0160 #define RUE_BYTEMASK2 0x0168 #define RUE_BYTEMASK3 0x0170 #define RUE_BYTEMASK4 0x0178 #define RUE_PHY1 0x0180 #define RUE_PHY2 0x0184 #define RUE_TW1 0x0186 #define RUE_REG_MIN 0x0120 #define RUE_REG_MAX 0x0189 /* EEPROM address declarations. */ #define RUE_EEPROM_BASE 0x1200 #define RUE_EEPROM_IDR0 (RUE_EEPROM_BASE + 0x02) #define RUE_EEPROM_IDR1 (RUE_EEPROM_BASE + 0x03) #define RUE_EEPROM_IDR2 (RUE_EEPROM_BASE + 0x03) #define RUE_EEPROM_IDR3 (RUE_EEPROM_BASE + 0x03) #define RUE_EEPROM_IDR4 (RUE_EEPROM_BASE + 0x03) #define RUE_EEPROM_IDR5 (RUE_EEPROM_BASE + 0x03) #define RUE_EEPROM_INTERVAL (RUE_EEPROM_BASE + 0x17) #define RUE_RXSTAT_VALID (0x01 << 12) #define RUE_RXSTAT_RUNT (0x02 << 12) #define RUE_RXSTAT_PMATCH (0x04 << 12) #define RUE_RXSTAT_MCAST (0x08 << 12) #define GET_MII(sc) ((sc)->sc_miibus ? \ device_get_softc((sc)->sc_miibus) : NULL) struct rue_intrpkt { uint8_t rue_tsr; uint8_t rue_rsr; uint8_t rue_gep_msr; uint8_t rue_waksr; uint8_t rue_txok_cnt; uint8_t rue_rxlost_cnt; uint8_t rue_crcerr_cnt; uint8_t rue_col_cnt; } __packed; struct rue_type { uint16_t rue_vid; uint16_t rue_did; }; struct rue_softc { struct usbd_config_td sc_config_td; struct __callout sc_watchdog; struct mtx sc_mtx; struct ifnet *sc_ifp; struct usbd_device *sc_udev; struct usbd_xfer *sc_xfer[RUE_ENDPT_MAX]; device_t sc_miibus; device_t sc_dev; uint32_t sc_unit; uint32_t sc_media_active; uint32_t sc_media_status; uint16_t sc_flags; #define RUE_FLAG_WAIT_LINK 0x0001 #define RUE_FLAG_INTR_STALL 0x0002 #define RUE_FLAG_READ_STALL 0x0004 #define RUE_FLAG_WRITE_STALL 0x0008 #define RUE_FLAG_LL_READY 0x0010 #define RUE_FLAG_HL_READY 0x0020 uint8_t sc_name[16]; }; struct rue_config_copy { uint32_t if_flags; uint32_t if_hashes[2]; uint8_t if_lladdr[ETHER_ADDR_LEN]; }; pwcbsd/usb/if_udav.c000644 000423 000000 00000077525 10551670753 015201 0ustar00luigiwheel000000 000000 /* $NetBSD: if_udav.c,v 1.2 2003/09/04 15:17:38 tsutsui Exp $ */ /* $nabe: if_udav.c,v 1.3 2003/08/21 16:57:19 nabe Exp $ */ /* $FreeBSD: src/sys/dev/usb/if_udav.c,v 1.23 2006/09/07 00:06:41 imp Exp $ */ /*- * Copyright (c) 2003 * Shingo WATANABE . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ /* * DM9601(DAVICOM USB to Ethernet MAC Controller with Integrated 10/100 PHY) * The spec can be found at the following url. * http://www.davicom.com.tw/big5/download/Data%20Sheet/DM9601-DS-P01-930914.pdf */ /* * NOTE: all function names beginning like "udav_cfg_" can only * be called from within the config thread function ! */ /* * TODO: * Interrupt Endpoint support * External PHYs * powerhook() support? */ #include __FBSDID("$FreeBSD: src/sys/dev/usb/if_udav.c,v 1.23 2006/09/07 00:06:41 imp Exp $"); #include "opt_inet.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define usbd_config_td_cc udav_config_copy #define usbd_config_td_softc udav_softc #include #include #include #include "usbdevs.h" #include #include /* "device miibus" required. See GENERIC if you get errors here. */ #include "miibus_if.h" #include /* prototypes */ static device_probe_t udav_probe; static device_attach_t udav_attach; static device_detach_t udav_detach; static device_shutdown_t udav_shutdown; static void udav_cfg_first_time_setup(struct udav_softc *sc, struct udav_config_copy *cc, u_int16_t refcount); static void udav_cfg_do_request(struct udav_softc *sc, usb_device_request_t *req, void *data); static void udav_cfg_csr_read(struct udav_softc *sc, u_int16_t offset, void *buf, u_int16_t len); static void udav_cfg_csr_write(struct udav_softc *sc, u_int16_t offset, void *buf, u_int16_t len); static u_int8_t udav_cfg_csr_read1(struct udav_softc *sc, u_int16_t offset); static void udav_cfg_csr_write1(struct udav_softc *sc, u_int16_t offset, u_int8_t ch); static void udav_init_cb(void *arg); static void udav_cfg_init(struct udav_softc *sc, struct udav_config_copy *cc, u_int16_t refcount); static void udav_cfg_reset(struct udav_softc *sc); static void udav_config_copy(struct udav_softc *sc, struct udav_config_copy *cc, u_int16_t refcount); static void udav_cfg_promisc_upd(struct udav_softc *sc, struct udav_config_copy *cc, u_int16_t refcount); static void udav_start_cb(struct ifnet *ifp); static void udav_start_transfers(struct udav_softc *sc); static void udav_bulk_write_clear_stall_callback(struct usbd_xfer *xfer); static void udav_bulk_write_callback(struct usbd_xfer *xfer); static void udav_bulk_read_clear_stall_callback(struct usbd_xfer *xfer); static void udav_bulk_read_callback(struct usbd_xfer *xfer); static void udav_intr_clear_stall_callback(struct usbd_xfer *xfer); static void udav_intr_callback(struct usbd_xfer *xfer); static int udav_ioctl_cb(struct ifnet *ifp, u_long cmd, caddr_t data); static void udav_watchdog(void *arg); static void udav_cfg_stop(struct udav_softc *sc, struct udav_config_copy *cc, u_int16_t refcount); static int udav_ifmedia_change_cb(struct ifnet *ifp); static void udav_cfg_ifmedia_change(struct udav_softc *sc, struct udav_config_copy *cc, u_int16_t refcount); static void udav_ifmedia_status_cb(struct ifnet *ifp, struct ifmediareq *ifmr); static void udav_cfg_tick(struct udav_softc *sc, struct udav_config_copy *cc, u_int16_t refcount); static int udav_cfg_miibus_readreg(device_t dev, int phy, int reg); static void udav_cfg_miibus_writereg(device_t dev, int phy, int reg, int data); static void udav_cfg_miibus_statchg(device_t dev); static const struct usbd_config udav_config[UDAV_ENDPT_MAX] = { [0] = { .type = UE_BULK, .endpoint = -1, /* any */ .direction = UE_DIR_OUT, .bufsize = (MCLBYTES + 2), .flags = (USBD_USE_DMA|USBD_FORCE_SHORT_XFER), .callback = &udav_bulk_write_callback, .timeout = 10000, /* 10 seconds */ }, [1] = { .type = UE_BULK, .endpoint = -1, /* any */ .direction = UE_DIR_IN, .bufsize = (MCLBYTES + 3), .flags = (USBD_USE_DMA|USBD_SHORT_XFER_OK), .callback = &udav_bulk_read_callback, .timeout = 0, /* no timeout */ }, [2] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .flags = USBD_USE_DMA, .callback = &udav_bulk_write_clear_stall_callback, .timeout = 1000, /* 1 second */ }, [3] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .flags = USBD_USE_DMA, .callback = &udav_bulk_read_clear_stall_callback, .timeout = 1000, /* 1 second */ }, [4] = { .type = UE_INTERRUPT, .endpoint = -1, /* any */ .direction = UE_DIR_IN, .flags = USBD_SHORT_XFER_OK, .bufsize = 0, /* use wMaxPacketSize */ .callback = &udav_intr_callback, }, [5] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .flags = USBD_USE_DMA, .callback = &udav_intr_clear_stall_callback, .timeout = 1000, /* 1 second */ }, }; static device_method_t udav_methods[] = { /* Device interface */ DEVMETHOD(device_probe, udav_probe), DEVMETHOD(device_attach, udav_attach), DEVMETHOD(device_detach, udav_detach), DEVMETHOD(device_shutdown, udav_shutdown), /* bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_driver_added, bus_generic_driver_added), /* MII interface */ DEVMETHOD(miibus_readreg, udav_cfg_miibus_readreg), DEVMETHOD(miibus_writereg, udav_cfg_miibus_writereg), DEVMETHOD(miibus_statchg, udav_cfg_miibus_statchg), { 0, 0 } }; static driver_t udav_driver = { .name = "udav", .methods = udav_methods, .size = sizeof(struct udav_softc), }; static devclass_t udav_devclass; DRIVER_MODULE(udav, uhub, udav_driver, udav_devclass, usbd_driver_load, 0); DRIVER_MODULE(miibus, udav, miibus_driver, miibus_devclass, 0, 0); MODULE_DEPEND(udav, usb, 1, 1, 1); MODULE_DEPEND(udav, ether, 1, 1, 1); MODULE_DEPEND(udav, miibus, 1, 1, 1); #ifdef USB_DEBUG #define DPRINTF(sc,n,fmt,...) \ do { if (udav_debug > (n)) { \ printf("%s:%s: " fmt, (sc)->sc_name, \ __FUNCTION__,## __VA_ARGS__); } } while (0) static int udav_debug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, udav, CTLFLAG_RW, 0, "USB udav"); SYSCTL_INT(_hw_usb_udav, OID_AUTO, debug, CTLFLAG_RW, &udav_debug, 0, "udav debug level"); #else #define DPRINTF(...) #endif #define UDAV_CFG_SETBIT(sc, reg, x) \ udav_cfg_csr_write1(sc, reg, udav_cfg_csr_read1(sc, reg) | (x)) #define UDAV_CFG_CLRBIT(sc, reg, x) \ udav_cfg_csr_write1(sc, reg, udav_cfg_csr_read1(sc, reg) & ~(x)) static const struct udav_type { struct usb_devno udav_dev; u_int16_t udav_flags; } udav_devs [] = { /* Corega USB-TXC */ {{ USB_VENDOR_COREGA, USB_PRODUCT_COREGA_FETHER_USB_TXC }, 0}, #if 0 /* DAVICOM DM9601 Generic? */ /* XXX: The following ids was obtained from the data sheet. */ {{ 0x0a46, 0x9601 }, 0}, #endif }; #define udav_lookup(v, p) ((const struct udav_type *)usb_lookup(udav_devs, v, p)) static int udav_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); if (uaa->iface != NULL) { return UMATCH_NONE; } return ((udav_lookup(uaa->vendor, uaa->product) != NULL) ? UMATCH_VENDOR_PRODUCT : UMATCH_NONE); } static int udav_attach(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct udav_softc *sc = device_get_softc(dev); int32_t error; if (sc == NULL) { return ENOMEM; } sc->sc_udev = uaa->device; sc->sc_dev = dev; sc->sc_unit = device_get_unit(dev); sc->sc_flags = udav_lookup(uaa->vendor, uaa->product)->udav_flags; usbd_set_desc(dev, uaa->device); snprintf(sc->sc_name, sizeof(sc->sc_name), "%s", device_get_nameunit(dev)); mtx_init(&(sc->sc_mtx), "udav lock", NULL, MTX_DEF | MTX_RECURSE); __callout_init_mtx(&(sc->sc_watchdog), &(sc->sc_mtx), CALLOUT_RETURNUNLOCKED); error = usbd_set_config_no(uaa->device, UDAV_CONFIG_NO, 1); if (error) { device_printf(dev, "setting config " "number failed!\n"); goto detach; } error = usbd_transfer_setup(uaa->device, UDAV_IFACE_INDEX, sc->sc_xfer, udav_config, UDAV_ENDPT_MAX, sc, &(sc->sc_mtx)); if (error) { device_printf(dev, "allocating USB " "transfers failed!\n"); goto detach; } error = usbd_config_td_setup(&(sc->sc_config_td), sc, &(sc->sc_mtx), &udav_config_copy, NULL, sizeof(struct udav_config_copy), 16); if (error) { device_printf(dev, "could not setup config " "thread!\n"); goto detach; } mtx_lock(&(sc->sc_mtx)); sc->sc_flags |= UDAV_FLAG_WAIT_LINK; /* start setup */ usbd_config_td_queue_command (&(sc->sc_config_td), &udav_cfg_first_time_setup, 0); /* start watchdog (will exit mutex) */ udav_watchdog(sc); return 0; /* success */ detach: udav_detach(dev); return ENXIO; /* failure */ } static void udav_cfg_first_time_setup(struct udav_softc *sc, struct udav_config_copy *cc, u_int16_t refcount) { struct ifnet * ifp; int error; u_int8_t eaddr[min(ETHER_ADDR_LEN,6)]; if (cc == NULL) { return; } /* reset the adapter */ udav_cfg_reset(sc); /* get Ethernet Address */ udav_cfg_csr_read(sc, UDAV_PAR, eaddr, ETHER_ADDR_LEN); mtx_unlock(&(sc->sc_mtx)); ifp = if_alloc(IFT_ETHER); mtx_lock(&(sc->sc_mtx)); if (ifp == NULL) { printf("%s: could not if_alloc()\n", sc->sc_name); goto done; } ifp->if_softc = sc; if_initname(ifp, "udav", sc->sc_unit); ifp->if_mtu = ETHERMTU; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_start = udav_start_cb; ifp->if_ioctl = udav_ioctl_cb; ifp->if_watchdog = NULL; ifp->if_init = udav_init_cb; ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; /* XXX need Giant when accessing * the device structures ! */ mtx_unlock(&(sc->sc_mtx)); mtx_lock(&Giant); error = mii_phy_probe(sc->sc_dev, &(sc->sc_miibus), &udav_ifmedia_change_cb, &udav_ifmedia_status_cb); mtx_unlock(&Giant); mtx_lock(&(sc->sc_mtx)); if (error) { printf("%s: MII without any PHY!\n", sc->sc_name); if_free(ifp); goto done; } sc->sc_ifp = ifp; /* * Call MI attach routine. */ ether_ifattach(ifp, eaddr); done: return; } static int udav_detach(device_t dev) { struct udav_softc * sc = device_get_softc(dev); struct ifnet * ifp; mtx_lock(&(sc->sc_mtx)); usbd_config_td_stop(&(sc->sc_config_td)); __callout_stop(&sc->sc_watchdog); udav_cfg_stop(sc, NULL, 0); ifp = sc->sc_ifp; mtx_unlock(&(sc->sc_mtx)); /* get rid of any late children */ bus_generic_detach(dev); if (ifp) { ether_ifdetach(ifp); if_free(ifp); } usbd_transfer_unsetup(sc->sc_xfer, UDAV_ENDPT_MAX); usbd_config_td_unsetup(&(sc->sc_config_td)); __callout_drain(&(sc->sc_watchdog)); mtx_destroy(&(sc->sc_mtx)); return 0; } static void udav_cfg_do_request(struct udav_softc *sc, usb_device_request_t *req, void *data) { u_int16_t length; usbd_status err; if (usbd_config_td_is_gone(&(sc->sc_config_td))) { goto error; } err = usbd_do_request_flags_mtx(sc->sc_udev, &(sc->sc_mtx), req, data, 0, NULL, 1000); if (err) { DPRINTF(sc, 0, "device request failed, err=%s " "(ignored)\n", usbd_errstr(err)); error: length = UGETW(req->wLength); if ((req->bmRequestType & UT_READ) && length) { bzero(data, length); } } return; } #if 0 static void udav_cfg_mem_read(struct udav_softc *sc, u_int16_t offset, void *buf, u_int16_t len) { usb_device_request_t req; len &= 0xff; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = UDAV_REQ_MEM_READ; USETW(req.wValue, 0x0000); USETW(req.wIndex, offset); USETW(req.wLength, len); udav_cfg_do_request(sc, &req, buf); return; } static void udav_cfg_mem_write(struct udav_softc *sc, u_int16_t offset, void *buf, u_int16_t len) { usb_device_request_t req; len &= 0xff; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = UDAV_REQ_MEM_WRITE; USETW(req.wValue, 0x0000); USETW(req.wIndex, offset); USETW(req.wLength, len); udav_cfg_do_request(sc, &req, buf); return; } static void udav_cfg_mem_write1(struct udav_softc *sc, u_int16_t offset, u_int8_t ch) { usb_device_request_t req; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = UDAV_REQ_MEM_WRITE1; USETW(req.wValue, ch); USETW(req.wIndex, offset); USETW(req.wLength, 0x0000); udav_cfg_do_request(sc, &req, NULL); return; } #endif static void udav_cfg_csr_read(struct udav_softc *sc, u_int16_t offset, void *buf, u_int16_t len) { usb_device_request_t req; len &= 0xff; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = UDAV_REQ_REG_READ; USETW(req.wValue, 0x0000); USETW(req.wIndex, offset); USETW(req.wLength, len); udav_cfg_do_request(sc, &req, buf); return; } static void udav_cfg_csr_write(struct udav_softc *sc, u_int16_t offset, void *buf, u_int16_t len) { usb_device_request_t req; offset &= 0xff; len &= 0xff; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = UDAV_REQ_REG_WRITE; USETW(req.wValue, 0x0000); USETW(req.wIndex, offset); USETW(req.wLength, len); udav_cfg_do_request(sc, &req, buf); return; } static u_int8_t udav_cfg_csr_read1(struct udav_softc *sc, u_int16_t offset) { u_int8_t val; udav_cfg_csr_read(sc, offset, &val, 1); return val; } static void udav_cfg_csr_write1(struct udav_softc *sc, u_int16_t offset, u_int8_t ch) { usb_device_request_t req; offset &= 0xff; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = UDAV_REQ_REG_WRITE1; USETW(req.wValue, ch); USETW(req.wIndex, offset); USETW(req.wLength, 0x0000); udav_cfg_do_request(sc, &req, NULL); return; } static void udav_init_cb(void *arg) { struct udav_softc *sc = arg; mtx_lock(&(sc->sc_mtx)); usbd_config_td_queue_command (&(sc->sc_config_td), &udav_cfg_init, 0); mtx_unlock(&(sc->sc_mtx)); return; } static void udav_cfg_init(struct udav_softc *sc, struct udav_config_copy *cc, u_int16_t refcount) { struct mii_data *mii = GET_MII(sc); if (cc == NULL) { /* immediate configuration */ struct ifnet *ifp = sc->sc_ifp; udav_cfg_stop(sc, NULL, 0); ifp->if_drv_flags |= IFF_DRV_RUNNING; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; sc->sc_flags |= UDAV_FLAG_HL_READY; return; } /* * Cancel pending I/O */ udav_cfg_stop(sc, cc, 0); /* set MAC address */ udav_cfg_csr_write(sc, UDAV_PAR, cc->if_lladdr, ETHER_ADDR_LEN); /* initialize network control register */ /* disable loopback */ UDAV_CFG_CLRBIT(sc, UDAV_NCR, UDAV_NCR_LBK0 | UDAV_NCR_LBK1); /* Initialize RX control register */ UDAV_CFG_SETBIT(sc, UDAV_RCR, UDAV_RCR_DIS_LONG | UDAV_RCR_DIS_CRC); /* load multicast filter and update promiscious mode bit */ udav_cfg_promisc_upd(sc, cc, 0); /* enable RX */ UDAV_CFG_SETBIT(sc, UDAV_RCR, UDAV_RCR_RXEN); /* clear POWER_DOWN state of internal PHY */ UDAV_CFG_SETBIT(sc, UDAV_GPCR, UDAV_GPCR_GEP_CNTL0); UDAV_CFG_CLRBIT(sc, UDAV_GPR, UDAV_GPR_GEPIO0); mii_mediachg(mii); sc->sc_flags |= (UDAV_FLAG_READ_STALL| UDAV_FLAG_WRITE_STALL| UDAV_FLAG_LL_READY); udav_start_transfers(sc); return; } static void udav_cfg_reset(struct udav_softc *sc) { usbd_status err; u_int16_t to; /* Select PHY */ #if 1 /* * XXX: force select internal phy. * external phy routines are not tested. */ UDAV_CFG_CLRBIT(sc, UDAV_NCR, UDAV_NCR_EXT_PHY); #else if (sc->sc_flags & UDAV_EXT_PHY) { UDAV_CFG_SETBIT(sc, UDAV_NCR, UDAV_NCR_EXT_PHY); } else { UDAV_CFG_CLRBIT(sc, UDAV_NCR, UDAV_NCR_EXT_PHY); } #endif UDAV_CFG_SETBIT(sc, UDAV_NCR, UDAV_NCR_RST); for (to = 0; ; to++) { if (to < 100) { err = usbd_config_td_sleep(&(sc->sc_config_td), hz/100); if (err) { break; } if (!(udav_cfg_csr_read1(sc, UDAV_NCR) & UDAV_NCR_RST)) { break; } } else { printf("%s: reset timeout!\n", sc->sc_name); break; } } err = usbd_config_td_sleep(&(sc->sc_config_td), hz/100); return; } #define UDAV_BITS 6 #define UDAV_CALCHASH(addr) \ (ether_crc32_le((addr), ETHER_ADDR_LEN) & ((1 << UDAV_BITS) - 1)) static void udav_config_copy(struct udav_softc *sc, struct udav_config_copy *cc, u_int16_t refcount) { struct ifnet * ifp = sc->sc_ifp; struct ifmultiaddr * ifma; u_int8_t h; u_int8_t i; bzero(cc, sizeof(*cc)); if (ifp) { for (i = 0; i < ETHER_ADDR_LEN; i++) { cc->if_lladdr[i] = IF_LLADDR(ifp)[i]; } cc->if_flags = ifp->if_flags; cc->if_hashes[7] |= 0x80; /* broadcast address */ IF_ADDR_LOCK(ifp); TAILQ_FOREACH(ifma, &(ifp->if_multiaddrs), ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) { continue; } h = UDAV_CALCHASH(LLADDR((struct sockaddr_dl *) ifma->ifma_addr)); cc->if_hashes[h>>3] |= 1 << (h & 0x7); } IF_ADDR_UNLOCK(ifp); } return; } static void udav_cfg_promisc_upd(struct udav_softc *sc, struct udav_config_copy *cc, u_int16_t refcount) { u_int8_t rxmode; if (cc == NULL) { /* nothing to do */ return; } rxmode = udav_cfg_csr_read1(sc, UDAV_RCR); rxmode &= ~(UDAV_RCR_ALL|UDAV_RCR_PRMSC); if (cc->if_flags & IFF_PROMISC) { rxmode |= UDAV_RCR_ALL|UDAV_RCR_PRMSC; } else if (cc->if_flags & IFF_ALLMULTI) { rxmode |= UDAV_RCR_ALL; } /* write hash value to the register */ udav_cfg_csr_write(sc, UDAV_MAR, cc->if_hashes, sizeof(cc->if_hashes)); /* write new mode bits */ udav_cfg_csr_write1(sc, UDAV_RCR, rxmode); return; } static void udav_start_cb(struct ifnet *ifp) { struct udav_softc *sc = ifp->if_softc; mtx_lock(&(sc->sc_mtx)); udav_start_transfers(sc); mtx_unlock(&(sc->sc_mtx)); return; } static void udav_start_transfers(struct udav_softc *sc) { if ((sc->sc_flags & UDAV_FLAG_LL_READY) && (sc->sc_flags & UDAV_FLAG_HL_READY)) { /* start the USB transfers, * if not already started: */ usbd_transfer_start(sc->sc_xfer[4]); usbd_transfer_start(sc->sc_xfer[1]); usbd_transfer_start(sc->sc_xfer[0]); } return; } static void udav_bulk_write_clear_stall_callback(struct usbd_xfer *xfer) { struct udav_softc *sc = xfer->priv_sc; struct usbd_xfer *xfer_other = sc->sc_xfer[0]; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, xfer_other); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, xfer_other); sc->sc_flags &= ~UDAV_FLAG_WRITE_STALL; usbd_transfer_start(xfer_other); return; tr_error: /* bomb out */ sc->sc_flags &= ~UDAV_FLAG_WRITE_STALL; DPRINTF(sc, 0, "bulk write pipe stopped\n"); return; } static void udav_bulk_write_callback(struct usbd_xfer *xfer) { struct udav_softc *sc = xfer->priv_sc; struct ifnet *ifp = sc->sc_ifp; struct mbuf *m; u_int32_t extra_len; u_int8_t buf[2]; USBD_CHECK_STATUS(xfer); tr_error: DPRINTF(sc, 10, "transfer error, %s\n", usbd_errstr(xfer->error)); if (xfer->error != USBD_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= UDAV_FLAG_WRITE_STALL; usbd_transfer_start(sc->sc_xfer[2]); } ifp->if_oerrors++; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; return; tr_transferred: DPRINTF(sc, 10, "transfer complete\n"); ifp->if_opackets++; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; tr_setup: if (sc->sc_flags & UDAV_FLAG_WRITE_STALL) { usbd_transfer_start(sc->sc_xfer[2]); goto done; } if (sc->sc_flags & UDAV_FLAG_WAIT_LINK) { /* don't send anything * if there is no link ! */ goto done; } IF_DEQUEUE(&(ifp->if_snd), m); if (m == NULL) { goto done; } if (m->m_pkthdr.len > MCLBYTES) { m->m_pkthdr.len = MCLBYTES; } if (m->m_pkthdr.len < UDAV_MIN_FRAME_LEN) { extra_len = UDAV_MIN_FRAME_LEN - m->m_pkthdr.len; } else { extra_len = 0; } xfer->length = (m->m_pkthdr.len + extra_len); /* * the frame length is specified * in the first 2 bytes of the buffer */ buf[0] = (u_int8_t)(xfer->length); buf[1] = (u_int8_t)(xfer->length >> 8); xfer->length += 2; usbd_copy_in(&(xfer->buf_data), 0, buf, 2); usbd_m_copy_in(&(xfer->buf_data), 2, m, 0, m->m_pkthdr.len); if (extra_len) { usbd_bzero(&(xfer->buf_data), xfer->length - extra_len, extra_len); } /* * if there's a BPF listener, bounce a copy * of this frame to him: */ BPF_MTAP(ifp, m); m_freem(m); usbd_start_hardware(xfer); ifp->if_drv_flags |= IFF_DRV_OACTIVE; done: return; } static void udav_bulk_read_clear_stall_callback(struct usbd_xfer *xfer) { struct udav_softc *sc = xfer->priv_sc; struct usbd_xfer *xfer_other = sc->sc_xfer[1]; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, xfer_other); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, xfer_other); sc->sc_flags &= ~UDAV_FLAG_READ_STALL; usbd_transfer_start(xfer_other); return; tr_error: /* bomb out */ sc->sc_flags &= ~UDAV_FLAG_READ_STALL; DPRINTF(sc, 0, "bulk read pipe stopped\n"); return; } static void udav_bulk_read_callback(struct usbd_xfer *xfer) { struct udav_softc *sc = xfer->priv_sc; struct ifnet *ifp = sc->sc_ifp; u_int8_t status; u_int16_t total_len; struct mbuf *m; USBD_CHECK_STATUS(xfer); tr_error: if (xfer->error != USBD_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= UDAV_FLAG_READ_STALL; usbd_transfer_start(sc->sc_xfer[3]); } DPRINTF(sc, 0, "bulk read error, %s\n", usbd_errstr(xfer->error)); return; tr_transferred: if (xfer->actlen < 1) { ifp->if_ierrors++; goto tr_setup; } xfer->actlen -= 1; usbd_copy_out(&(xfer->buf_data), 0, &status, 1); if (status & UDAV_RSR_LCS) { ifp->if_collisions++; goto tr_setup; } if ((status & UDAV_RSR_ERR) || (xfer->actlen < 2)) { ifp->if_ierrors++; goto tr_setup; } usbd_copy_out(&(xfer->buf_data), 1, &total_len, 2); total_len = le16toh(total_len); xfer->actlen -= 2; xfer->actlen = min(xfer->actlen, total_len); if (xfer->actlen < (sizeof(struct ether_header) + ETHER_CRC_LEN)) { ifp->if_ierrors++; goto tr_setup; } xfer->actlen -= ETHER_CRC_LEN; m = usbd_ether_get_mbuf(); if (m == NULL) { ifp->if_ierrors++; goto tr_setup; } xfer->actlen = min(xfer->actlen, m->m_len); usbd_copy_out(&(xfer->buf_data), 3, m->m_data, xfer->actlen); ifp->if_ipackets++; m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = m->m_len = xfer->actlen; (ifp->if_input)(ifp, m); tr_setup: if (sc->sc_flags & UDAV_FLAG_READ_STALL) { usbd_transfer_start(sc->sc_xfer[3]); } else { usbd_start_hardware(xfer); } return; } static void udav_intr_clear_stall_callback(struct usbd_xfer *xfer) { struct udav_softc *sc = xfer->priv_sc; struct usbd_xfer *xfer_other = sc->sc_xfer[4]; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, xfer_other); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, xfer_other); sc->sc_flags &= ~UDAV_FLAG_INTR_STALL; usbd_transfer_start(xfer_other); return; tr_error: /* bomb out */ sc->sc_flags &= ~UDAV_FLAG_INTR_STALL; DPRINTF(sc, 0, "interrupt read pipe stopped\n"); return; } static void udav_intr_callback(struct usbd_xfer *xfer) { struct udav_softc *sc = xfer->priv_sc; USBD_CHECK_STATUS(xfer); tr_transferred: tr_setup: if (sc->sc_flags & UDAV_FLAG_INTR_STALL) { usbd_transfer_start(sc->sc_xfer[5]); } else { usbd_start_hardware(xfer); } return; tr_error: if (xfer->error != USBD_CANCELLED) { /* start clear stall */ sc->sc_flags |= UDAV_FLAG_INTR_STALL; usbd_transfer_start(sc->sc_xfer[5]); } return; } static int udav_ioctl_cb(struct ifnet *ifp, u_long cmd, caddr_t data) { struct udav_softc * sc = ifp->if_softc; struct mii_data * mii; int error = 0; mtx_lock(&(sc->sc_mtx)); switch (cmd) { case SIOCSIFFLAGS: if (ifp->if_flags & IFF_UP) { if (ifp->if_drv_flags & IFF_DRV_RUNNING) { usbd_config_td_queue_command (&(sc->sc_config_td), &udav_cfg_promisc_upd, 0); } else { usbd_config_td_queue_command (&(sc->sc_config_td), &udav_cfg_init, 0); } } else { if (ifp->if_drv_flags & IFF_DRV_RUNNING) { usbd_config_td_queue_command (&(sc->sc_config_td), &udav_cfg_stop, 0); } } break; case SIOCADDMULTI: case SIOCDELMULTI: usbd_config_td_queue_command (&(sc->sc_config_td), &udav_cfg_promisc_upd, 0); break; case SIOCGIFMEDIA: case SIOCSIFMEDIA: mii = GET_MII(sc); if (mii == NULL) { error = EINVAL; } else { error = ifmedia_ioctl (ifp, (void *)data, &mii->mii_media, cmd); } break; default: error = ether_ioctl(ifp, cmd, data); break; } mtx_unlock(&(sc->sc_mtx)); return error; } static void udav_watchdog(void *arg) { struct udav_softc *sc = arg; mtx_assert(&(sc->sc_mtx), MA_OWNED); usbd_config_td_queue_command (&(sc->sc_config_td), &udav_cfg_tick, 0); __callout_reset(&(sc->sc_watchdog), hz, &udav_watchdog, sc); mtx_unlock(&(sc->sc_mtx)); return; } /* * NOTE: can be called when "ifp" is NULL */ static void udav_cfg_stop(struct udav_softc *sc, struct udav_config_copy *cc, u_int16_t refcount) { if (cc == NULL) { /* immediate configuration */ struct ifnet *ifp = sc->sc_ifp; if (ifp) { /* clear flags */ ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); } sc->sc_flags &= ~(UDAV_FLAG_HL_READY| UDAV_FLAG_LL_READY); sc->sc_flags |= UDAV_FLAG_WAIT_LINK; /* stop all the transfers, * if not already stopped: */ if (sc->sc_xfer[0]) { usbd_transfer_stop(sc->sc_xfer[0]); } if (sc->sc_xfer[1]) { usbd_transfer_stop(sc->sc_xfer[1]); } if (sc->sc_xfer[2]) { usbd_transfer_stop(sc->sc_xfer[2]); } if (sc->sc_xfer[3]) { usbd_transfer_stop(sc->sc_xfer[3]); } if (sc->sc_xfer[4]) { usbd_transfer_stop(sc->sc_xfer[4]); } if (sc->sc_xfer[5]) { usbd_transfer_stop(sc->sc_xfer[5]); } return; } udav_cfg_reset(sc); return; } static int udav_ifmedia_change_cb(struct ifnet *ifp) { struct udav_softc *sc = ifp->if_softc; mtx_lock(&(sc->sc_mtx)); usbd_config_td_queue_command (&(sc->sc_config_td), &udav_cfg_ifmedia_change, 0); mtx_unlock(&(sc->sc_mtx)); return 0; } static void udav_cfg_ifmedia_change(struct udav_softc *sc, struct udav_config_copy *cc, u_int16_t refcount) { struct ifnet * ifp = sc->sc_ifp; struct mii_data * mii = GET_MII(sc); if ((cc == NULL) || (ifp == NULL) || (mii == NULL)) { /* not ready */ return; } sc->sc_flags |= UDAV_FLAG_WAIT_LINK; if (mii->mii_instance) { struct mii_softc *miisc; LIST_FOREACH(miisc, &mii->mii_phys, mii_list) { mii_phy_reset(miisc); } } mii_mediachg(mii); return; } static void udav_ifmedia_status_cb(struct ifnet *ifp, struct ifmediareq *ifmr) { struct udav_softc *sc = ifp->if_softc; mtx_lock(&(sc->sc_mtx)); if (ifp->if_drv_flags & IFF_DRV_RUNNING) { ifmr->ifm_active = sc->sc_media_active; ifmr->ifm_status = sc->sc_media_status; } else { ifmr->ifm_active = IFM_ETHER | IFM_NONE; ifmr->ifm_status = 0; } mtx_unlock(&(sc->sc_mtx)); return; } static void udav_cfg_tick(struct udav_softc *sc, struct udav_config_copy *cc, u_int16_t refcount) { struct ifnet * ifp = sc->sc_ifp; struct mii_data * mii = GET_MII(sc); if ((cc == NULL) || (ifp == NULL) || (mii == NULL)) { /* not ready */ return; } mii_tick(mii); mii_pollstat(mii); if ((sc->sc_flags & UDAV_FLAG_WAIT_LINK) && (mii->mii_media_status & IFM_ACTIVE) && (IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE)) { sc->sc_flags &= ~UDAV_FLAG_WAIT_LINK; } sc->sc_media_active = mii->mii_media_active; sc->sc_media_status = mii->mii_media_status; /* start stopped transfers, if any */ udav_start_transfers(sc); return; } static int udav_cfg_miibus_readreg(device_t dev, int phy, int reg) { struct udav_softc *sc = device_get_softc(dev); u_int8_t val[2]; u_int16_t data16; /* XXX: one PHY only for the internal PHY */ if (phy != 0) { return 0; } mtx_lock(&(sc->sc_mtx)); /* XXX */ /* select internal PHY and set PHY register address */ udav_cfg_csr_write1(sc, UDAV_EPAR, UDAV_EPAR_PHY_ADR0 | (reg & UDAV_EPAR_EROA_MASK)); /* select PHY operation and start read command */ udav_cfg_csr_write1(sc, UDAV_EPCR, UDAV_EPCR_EPOS | UDAV_EPCR_ERPRR); /* XXX: should we wait? */ /* end read command */ UDAV_CFG_CLRBIT(sc, UDAV_EPCR, UDAV_EPCR_ERPRR); /* retrieve the result from data registers */ udav_cfg_csr_read(sc, UDAV_EPDRL, val, 2); mtx_unlock(&(sc->sc_mtx)); /* XXX */ data16 = (val[0] | (val[1] << 8)); DPRINTF(sc, 10, "phy=%d reg=0x%04x => 0x%04x\n", phy, reg, data16); return data16; } static void udav_cfg_miibus_writereg(device_t dev, int phy, int reg, int data) { struct udav_softc *sc = device_get_softc(dev); u_int8_t val[2]; /* XXX: one PHY only for the internal PHY */ if (phy != 0) { return; } mtx_lock(&(sc->sc_mtx)); /* XXX */ /* select internal PHY and set PHY register address */ udav_cfg_csr_write1(sc, UDAV_EPAR, UDAV_EPAR_PHY_ADR0 | (reg & UDAV_EPAR_EROA_MASK)); /* put the value to the data registers */ val[0] = (data & 0xff); val[1] = (data >> 8) & 0xff; udav_cfg_csr_write(sc, UDAV_EPDRL, val, 2); /* select PHY operation and start write command */ udav_cfg_csr_write1(sc, UDAV_EPCR, UDAV_EPCR_EPOS | UDAV_EPCR_ERPRW); /* XXX: should we wait? */ /* end write command */ UDAV_CFG_CLRBIT(sc, UDAV_EPCR, UDAV_EPCR_ERPRW); mtx_unlock(&(sc->sc_mtx)); /* XXX */ return; } static void udav_cfg_miibus_statchg(device_t dev) { /* nothing to do */ return; } /* * Stop all chip I/O so that the kernel's probe routines don't * get confused by errant DMAs when rebooting. */ static int udav_shutdown(device_t dev) { struct udav_softc *sc = device_get_softc(dev); mtx_lock(&(sc->sc_mtx)); usbd_config_td_queue_command (&(sc->sc_config_td), &udav_cfg_stop, 0); mtx_unlock(&(sc->sc_mtx)); return 0; } pwcbsd/usb/if_udavreg.h000644 000423 000000 00000016246 10551670753 015675 0ustar00luigiwheel000000 000000 /* $NetBSD: if_udavreg.h,v 1.2 2003/09/04 15:17:39 tsutsui Exp $ */ /* $nabe: if_udavreg.h,v 1.2 2003/08/21 16:26:40 nabe Exp $ */ /* $FreeBSD: src/sys/dev/usb/if_udavreg.h,v 1.7 2006/09/07 00:06:41 imp Exp $ */ /*- * Copyright (c) 2003 * Shingo WATANABE . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #define UDAV_IFACE_INDEX 0 #define UDAV_CONFIG_NO 1 #define UDAV_ENDPT_MAX 6 /* units */ /* Packet length */ #define UDAV_MIN_FRAME_LEN 60 /* Request */ #define UDAV_REQ_REG_READ 0x00 /* Read from register(s) */ #define UDAV_REQ_REG_WRITE 0x01 /* Write to register(s) */ #define UDAV_REQ_REG_WRITE1 0x03 /* Write to a register */ #define UDAV_REQ_MEM_READ 0x02 /* Read from memory */ #define UDAV_REQ_MEM_WRITE 0x05 /* Write to memory */ #define UDAV_REQ_MEM_WRITE1 0x07 /* Write a byte to memory */ /* Registers */ #define UDAV_NCR 0x00 /* Network Control Register */ #define UDAV_NCR_EXT_PHY (1<<7) /* Select External PHY */ #define UDAV_NCR_WAKEEN (1<<6) /* Wakeup Event Enable */ #define UDAV_NCR_FCOL (1<<4) /* Force Collision Mode */ #define UDAV_NCR_FDX (1<<3) /* Full-Duplex Mode (RO on Int. PHY) */ #define UDAV_NCR_LBK1 (1<<2) /* Lookback Mode */ #define UDAV_NCR_LBK0 (1<<1) /* Lookback Mode */ #define UDAV_NCR_RST (1<<0) /* Software reset */ #define UDAV_RCR 0x05 /* RX Control Register */ #define UDAV_RCR_WTDIS (1<<6) /* Watchdog Timer Disable */ #define UDAV_RCR_DIS_LONG (1<<5) /* Discard Long Packet(over 1522Byte) */ #define UDAV_RCR_DIS_CRC (1<<4) /* Discard CRC Error Packet */ #define UDAV_RCR_ALL (1<<3) /* Pass All Multicast */ #define UDAV_RCR_RUNT (1<<2) /* Pass Runt Packet */ #define UDAV_RCR_PRMSC (1<<1) /* Promiscuous Mode */ #define UDAV_RCR_RXEN (1<<0) /* RX Enable */ #define UDAV_RSR 0x06 /* RX Status Register */ #define UDAV_RSR_RF (1<<7) /* Runt Frame */ #define UDAV_RSR_MF (1<<6) /* Multicast Frame */ #define UDAV_RSR_LCS (1<<5) /* Late Collision Seen */ #define UDAV_RSR_RWTO (1<<4) /* Receive Watchdog Time-Out */ #define UDAV_RSR_PLE (1<<3) /* Physical Layer Error */ #define UDAV_RSR_AE (1<<2) /* Alignment Error */ #define UDAV_RSR_CE (1<<1) /* CRC Error */ #define UDAV_RSR_FOE (1<<0) /* FIFO Overflow Error */ #define UDAV_RSR_ERR (UDAV_RSR_RF | UDAV_RSR_LCS | \ UDAV_RSR_RWTO | UDAV_RSR_PLE | \ UDAV_RSR_AE | UDAV_RSR_CE | UDAV_RSR_FOE) #define UDAV_EPCR 0x0b /* EEPROM & PHY Control Register */ #define UDAV_EPCR_REEP (1<<5) /* Reload EEPROM */ #define UDAV_EPCR_WEP (1<<4) /* Write EEPROM enable */ #define UDAV_EPCR_EPOS (1<<3) /* EEPROM or PHY Operation Select */ #define UDAV_EPCR_ERPRR (1<<2) /* EEPROM/PHY Register Read Command */ #define UDAV_EPCR_ERPRW (1<<1) /* EEPROM/PHY Register Write Command */ #define UDAV_EPCR_ERRE (1<<0) /* EEPROM/PHY Access Status */ #define UDAV_EPAR 0x0c /* EEPROM & PHY Control Register */ #define UDAV_EPAR_PHY_ADR1 (1<<7) /* PHY Address bit 1 */ #define UDAV_EPAR_PHY_ADR0 (1<<6) /* PHY Address bit 0 */ #define UDAV_EPAR_EROA (1<<0) /* EEPROM Word/PHY Register Address */ #define UDAV_EPAR_EROA_MASK (0x1f) /* [5:0] */ #define UDAV_EPDRL 0x0d /* EEPROM & PHY Data Register */ #define UDAV_EPDRH 0x0e /* EEPROM & PHY Data Register */ #define UDAV_PAR0 0x10 /* Ethernet Address, load from EEPROM */ #define UDAV_PAR1 0x11 /* Ethernet Address, load from EEPROM */ #define UDAV_PAR2 0x12 /* Ethernet Address, load from EEPROM */ #define UDAV_PAR3 0x13 /* Ethernet Address, load from EEPROM */ #define UDAV_PAR4 0x14 /* Ethernet Address, load from EEPROM */ #define UDAV_PAR5 0x15 /* Ethernet Address, load from EEPROM */ #define UDAV_PAR UDAV_PAR0 #define UDAV_MAR0 0x16 /* Multicast Register */ #define UDAV_MAR1 0x17 /* Multicast Register */ #define UDAV_MAR2 0x18 /* Multicast Register */ #define UDAV_MAR3 0x19 /* Multicast Register */ #define UDAV_MAR4 0x1a /* Multicast Register */ #define UDAV_MAR5 0x1b /* Multicast Register */ #define UDAV_MAR6 0x1c /* Multicast Register */ #define UDAV_MAR7 0x1d /* Multicast Register */ #define UDAV_MAR UDAV_MAR0 #define UDAV_GPCR 0x1e /* General purpose control register */ #define UDAV_GPCR_GEP_CNTL6 (1<<6) /* General purpose control 6 */ #define UDAV_GPCR_GEP_CNTL5 (1<<5) /* General purpose control 5 */ #define UDAV_GPCR_GEP_CNTL4 (1<<4) /* General purpose control 4 */ #define UDAV_GPCR_GEP_CNTL3 (1<<3) /* General purpose control 3 */ #define UDAV_GPCR_GEP_CNTL2 (1<<2) /* General purpose control 2 */ #define UDAV_GPCR_GEP_CNTL1 (1<<1) /* General purpose control 1 */ #define UDAV_GPCR_GEP_CNTL0 (1<<0) /* General purpose control 0 */ #define UDAV_GPR 0x1f /* General purpose register */ #define UDAV_GPR_GEPIO6 (1<<6) /* General purpose 6 */ #define UDAV_GPR_GEPIO5 (1<<5) /* General purpose 5 */ #define UDAV_GPR_GEPIO4 (1<<4) /* General purpose 4 */ #define UDAV_GPR_GEPIO3 (1<<3) /* General purpose 3 */ #define UDAV_GPR_GEPIO2 (1<<2) /* General purpose 2 */ #define UDAV_GPR_GEPIO1 (1<<1) /* General purpose 1 */ #define UDAV_GPR_GEPIO0 (1<<0) /* General purpose 0 */ #define GET_MII(sc) ((sc)->sc_miibus ? \ device_get_softc((sc)->sc_miibus) : NULL) struct udav_softc { struct usbd_config_td sc_config_td; struct __callout sc_watchdog; struct mtx sc_mtx; struct ifnet *sc_ifp; struct usbd_device *sc_udev; struct usbd_xfer *sc_xfer[UDAV_ENDPT_MAX]; device_t sc_miibus; device_t sc_dev; uint32_t sc_unit; uint32_t sc_media_active; uint32_t sc_media_status; uint16_t sc_flags; #define UDAV_FLAG_WAIT_LINK 0x0001 #define UDAV_FLAG_INTR_STALL 0x0002 #define UDAV_FLAG_READ_STALL 0x0004 #define UDAV_FLAG_WRITE_STALL 0x0008 #define UDAV_FLAG_LL_READY 0x0010 #define UDAV_FLAG_HL_READY 0x0020 #define UDAV_FLAG_EXT_PHY 0x0040 uint8_t sc_name[16]; }; struct udav_config_copy { uint32_t if_flags; uint8_t if_lladdr[ETHER_ADDR_LEN]; uint8_t if_hashes[8]; }; pwcbsd/usb/if_ural.c000644 000423 000000 00000210036 10551670753 015167 0ustar00luigiwheel000000 000000 /* $FreeBSD: src/sys/dev/usb/if_ural.c,v 1.45 2006/09/07 00:06:41 imp Exp $ */ /*- * Copyright (c) 2005, 2006 * Damien Bergamini * * Copyright (c) 2006 * Hans Petter Selasky * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * * NOTE: all function names beginning like "ural_cfg_" can only * be called from within the config thread function ! * * TODO: add support for raw transmit trough BPF. See: * http://www.freebsd.org/cgi/cvsweb.cgi/src/sys/dev/usb/if_ural.c.diff?r1=1.41&r2=1.42 */ #include __FBSDID("$FreeBSD: src/sys/dev/usb/if_ural.c,v 1.45 2006/09/07 00:06:41 imp Exp $"); /*- * Ralink Technology RT2500USB chipset driver * http://www.ralinktech.com/ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define usbd_config_td_cc ural_config_copy #define usbd_config_td_softc ural_softc #include #include #include #include "usbdevs.h" #include #include #ifdef USB_DEBUG #define DPRINTF(sc,n,fmt,...) \ do { if (ural_debug > (n)) { \ printf("%s:%s: " fmt, (sc)->sc_name, \ __FUNCTION__,## __VA_ARGS__); } } while (0) static int ural_debug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, ural, CTLFLAG_RW, 0, "USB ural"); SYSCTL_INT(_hw_usb_ural, OID_AUTO, debug, CTLFLAG_RW, &ural_debug, 0, "ural debug level"); #else #define DPRINTF(...) #endif /* prototypes */ static device_probe_t ural_probe; static device_attach_t ural_attach; static device_detach_t ural_detach; static void ural_cfg_do_request(struct ural_softc *sc, usb_device_request_t *req, void *data); static void ural_cfg_set_testmode(struct ural_softc *sc); static void ural_cfg_eeprom_read(struct ural_softc *sc, u_int16_t addr, void *buf, int len); static u_int16_t ural_cfg_read(struct ural_softc *sc, u_int16_t reg); static void ural_cfg_read_multi(struct ural_softc *sc, u_int16_t reg, void *buf, int len); static void ural_cfg_write(struct ural_softc *sc, u_int16_t reg, u_int16_t val); static void ural_cfg_write_multi(struct ural_softc *sc, u_int16_t reg, void *buf, int len); static void ural_cfg_bbp_write(struct ural_softc *sc, u_int8_t reg, u_int8_t val); static u_int8_t ural_cfg_bbp_read(struct ural_softc *sc, u_int8_t reg); static void ural_cfg_rf_write(struct ural_softc *sc, u_int8_t reg, u_int32_t val); static void ural_cfg_first_time_setup(struct ural_softc *sc, struct ural_config_copy *cc, u_int16_t refcount); static void ural_end_of_commands(struct ural_softc *sc); static void ural_config_copy(struct ural_softc *sc, struct ural_config_copy *cc, u_int16_t refcount); static const char * ural_get_rf(int rev); static int ural_rxrate(struct ural_rx_desc *desc); static void ural_bulk_read_callback(struct usbd_xfer *xfer); static void ural_bulk_read_clear_stall_callback(struct usbd_xfer *xfer); static u_int16_t ural_ack_rate(struct ieee80211com *ic, u_int16_t rate); static u_int16_t ural_txtime(struct ural_softc *sc, u_int16_t len, u_int16_t rate, u_int32_t flags); static u_int8_t ural_plcp_signal(u_int16_t rate); static void ural_setup_tx_desc(struct ural_softc *sc, u_int32_t flags, u_int16_t len, u_int16_t rate); static void ural_bulk_write_callback(struct usbd_xfer *xfer); static void ural_bulk_write_clear_stall_callback(struct usbd_xfer *xfer); static void ural_watchdog(void *arg); static void ural_init_cb(void *arg); static int ural_ioctl_cb(struct ifnet *ifp, u_long cmd, caddr_t data); static void ural_start_cb(struct ifnet *ifp); static int ural_media_change_cb(struct ifnet *ifp); static int ural_reset_cb(struct ifnet *ifp); static int ural_newstate_cb(struct ieee80211com *ic, enum ieee80211_state nstate, int arg); static void ural_tx_bcn_complete(struct ural_softc *sc); static void ural_cfg_tx_bcn(struct ural_softc *sc); static void ural_cfg_set_chan(struct ural_softc *sc, struct ural_config_copy *cc, u_int16_t refcount); static void ural_cfg_set_run(struct ural_softc *sc, struct ural_config_copy *cc, u_int16_t refcount); static void ural_cfg_disable_rf_tune(struct ural_softc *sc); static void ural_cfg_enable_tsf_sync(struct ural_softc *sc, struct ural_config_copy *cc, u_int16_t refcount); static void ural_cfg_disable_tsf_sync(struct ural_softc *sc, struct ural_config_copy *cc, u_int16_t refcount); static void ural_cfg_update_slot(struct ural_softc *sc, struct ural_config_copy *cc, u_int16_t refcount); static void ural_cfg_set_txpreamble(struct ural_softc *sc, struct ural_config_copy *cc, u_int16_t refcount); static void ural_cfg_set_basicrates(struct ural_softc *sc, struct ural_config_copy *cc, u_int16_t refcount); static void ural_cfg_set_bssid(struct ural_softc *sc, const u_int8_t *bssid); static void ural_cfg_set_macaddr(struct ural_softc *sc, const u_int8_t *addr); static void ural_cfg_update_promisc(struct ural_softc *sc, struct ural_config_copy *cc, u_int16_t refcount); static void ural_cfg_set_txantenna(struct ural_softc *sc, u_int8_t antenna); static void ural_cfg_set_rxantenna(struct ural_softc *sc, u_int8_t antenna); static void ural_cfg_read_eeprom(struct ural_softc *sc); static u_int8_t ural_cfg_bbp_init(struct ural_softc *sc); static void ural_cfg_init(struct ural_softc *sc, struct ural_config_copy *cc, u_int16_t refcount); static void ural_cfg_stop(struct ural_softc *sc, struct ural_config_copy *cc, u_int16_t refcount); static void ural_cfg_amrr_start(struct ural_softc *sc); static void ural_cfg_amrr_timeout(struct ural_softc *sc, struct ural_config_copy *cc, u_int16_t refcount); static void ural_ratectl(struct ural_amrr *amrr, struct ieee80211_node *ni); /* various supported device vendors/products */ static const struct usb_devno ural_devs[] = { { USB_VENDOR_ASUS, USB_PRODUCT_ASUS_WL167G }, { USB_VENDOR_ASUS, USB_PRODUCT_RALINK_RT2570 }, { USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D7050 }, { USB_VENDOR_CONCEPTRONIC, USB_PRODUCT_CONCEPTRONIC_C54U }, { USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DWLG122 }, { USB_VENDOR_GIGABYTE, USB_PRODUCT_GIGABYTE_GNWBKG }, { USB_VENDOR_GUILLEMOT, USB_PRODUCT_GUILLEMOT_HWGUSB254 }, { USB_VENDOR_LINKSYS4, USB_PRODUCT_LINKSYS4_WUSB54G }, { USB_VENDOR_LINKSYS4, USB_PRODUCT_LINKSYS4_WUSB54GP }, { USB_VENDOR_LINKSYS4, USB_PRODUCT_LINKSYS4_HU200TS }, { USB_VENDOR_MELCO, USB_PRODUCT_MELCO_KG54 }, { USB_VENDOR_MELCO, USB_PRODUCT_MELCO_KG54AI }, { USB_VENDOR_MELCO, USB_PRODUCT_MELCO_KG54YB }, { USB_VENDOR_MELCO, USB_PRODUCT_MELCO_NINWIFI }, { USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2570 }, { USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2570_2 }, { USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2570_3 }, { USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2570 }, { USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2570_2 }, { USB_VENDOR_VTECH, USB_PRODUCT_VTECH_RT2570 }, { USB_VENDOR_ZINWELL, USB_PRODUCT_ZINWELL_RT2570 } }; /* * Supported rates for 802.11a/b/g modes (in 500Kbps unit). */ static const struct ieee80211_rateset ural_rateset_11a = { 8, { 12, 18, 24, 36, 48, 72, 96, 108 } }; static const struct ieee80211_rateset ural_rateset_11b = { 4, { 2, 4, 11, 22 } }; static const struct ieee80211_rateset ural_rateset_11g = { 12, { 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 } }; /* * Default values for MAC registers; values taken from * the reference driver: */ static const struct { u_int16_t reg; u_int16_t val; } ural_def_mac[] = { { RAL_TXRX_CSR5, 0x8c8d }, { RAL_TXRX_CSR6, 0x8b8a }, { RAL_TXRX_CSR7, 0x8687 }, { RAL_TXRX_CSR8, 0x0085 }, { RAL_MAC_CSR13, 0x1111 }, { RAL_MAC_CSR14, 0x1e11 }, { RAL_TXRX_CSR21, 0xe78f }, { RAL_MAC_CSR9, 0xff1d }, { RAL_MAC_CSR11, 0x0002 }, { RAL_MAC_CSR22, 0x0053 }, { RAL_MAC_CSR15, 0x0000 }, { RAL_MAC_CSR8, 0x0780 }, { RAL_TXRX_CSR19, 0x0000 }, { RAL_TXRX_CSR18, 0x005a }, { RAL_PHY_CSR2, 0x0000 }, { RAL_TXRX_CSR0, 0x1ec0 }, { RAL_PHY_CSR4, 0x000f } }; /* * Default values for BBP registers; values taken from the reference driver. */ static const struct { u_int8_t reg; u_int8_t val; } ural_def_bbp[] = { { 3, 0x02 }, { 4, 0x19 }, { 14, 0x1c }, { 15, 0x30 }, { 16, 0xac }, { 17, 0x48 }, { 18, 0x18 }, { 19, 0xff }, { 20, 0x1e }, { 21, 0x08 }, { 22, 0x08 }, { 23, 0x08 }, { 24, 0x80 }, { 25, 0x50 }, { 26, 0x08 }, { 27, 0x23 }, { 30, 0x10 }, { 31, 0x2b }, { 32, 0xb9 }, { 34, 0x12 }, { 35, 0x50 }, { 39, 0xc4 }, { 40, 0x02 }, { 41, 0x60 }, { 53, 0x10 }, { 54, 0x18 }, { 56, 0x08 }, { 57, 0x10 }, { 58, 0x08 }, { 61, 0x60 }, { 62, 0x10 }, { 75, 0xff } }; /* * Default values for RF register R2 indexed by channel numbers. */ static const u_int32_t ural_rf2522_r2[] = { 0x307f6, 0x307fb, 0x30800, 0x30805, 0x3080a, 0x3080f, 0x30814, 0x30819, 0x3081e, 0x30823, 0x30828, 0x3082d, 0x30832, 0x3083e }; static const u_int32_t ural_rf2523_r2[] = { 0x00327, 0x00328, 0x00329, 0x0032a, 0x0032b, 0x0032c, 0x0032d, 0x0032e, 0x0032f, 0x00340, 0x00341, 0x00342, 0x00343, 0x00346 }; static const u_int32_t ural_rf2524_r2[] = { 0x00327, 0x00328, 0x00329, 0x0032a, 0x0032b, 0x0032c, 0x0032d, 0x0032e, 0x0032f, 0x00340, 0x00341, 0x00342, 0x00343, 0x00346 }; static const u_int32_t ural_rf2525_r2[] = { 0x20327, 0x20328, 0x20329, 0x2032a, 0x2032b, 0x2032c, 0x2032d, 0x2032e, 0x2032f, 0x20340, 0x20341, 0x20342, 0x20343, 0x20346 }; static const u_int32_t ural_rf2525_hi_r2[] = { 0x2032f, 0x20340, 0x20341, 0x20342, 0x20343, 0x20344, 0x20345, 0x20346, 0x20347, 0x20348, 0x20349, 0x2034a, 0x2034b, 0x2034e }; static const u_int32_t ural_rf2525e_r2[] = { 0x2044d, 0x2044e, 0x2044f, 0x20460, 0x20461, 0x20462, 0x20463, 0x20464, 0x20465, 0x20466, 0x20467, 0x20468, 0x20469, 0x2046b }; static const u_int32_t ural_rf2526_hi_r2[] = { 0x0022a, 0x0022b, 0x0022b, 0x0022c, 0x0022c, 0x0022d, 0x0022d, 0x0022e, 0x0022e, 0x0022f, 0x0022d, 0x00240, 0x00240, 0x00241 }; static const u_int32_t ural_rf2526_r2[] = { 0x00226, 0x00227, 0x00227, 0x00228, 0x00228, 0x00229, 0x00229, 0x0022a, 0x0022a, 0x0022b, 0x0022b, 0x0022c, 0x0022c, 0x0022d }; /* * For dual-band RF, RF registers R1 and R4 also depend on channel number; * values taken from the reference driver. */ static const struct { u_int8_t chan; u_int32_t r1; u_int32_t r2; u_int32_t r4; } ural_rf5222[] = { { 1, 0x08808, 0x0044d, 0x00282 }, { 2, 0x08808, 0x0044e, 0x00282 }, { 3, 0x08808, 0x0044f, 0x00282 }, { 4, 0x08808, 0x00460, 0x00282 }, { 5, 0x08808, 0x00461, 0x00282 }, { 6, 0x08808, 0x00462, 0x00282 }, { 7, 0x08808, 0x00463, 0x00282 }, { 8, 0x08808, 0x00464, 0x00282 }, { 9, 0x08808, 0x00465, 0x00282 }, { 10, 0x08808, 0x00466, 0x00282 }, { 11, 0x08808, 0x00467, 0x00282 }, { 12, 0x08808, 0x00468, 0x00282 }, { 13, 0x08808, 0x00469, 0x00282 }, { 14, 0x08808, 0x0046b, 0x00286 }, { 36, 0x08804, 0x06225, 0x00287 }, { 40, 0x08804, 0x06226, 0x00287 }, { 44, 0x08804, 0x06227, 0x00287 }, { 48, 0x08804, 0x06228, 0x00287 }, { 52, 0x08804, 0x06229, 0x00287 }, { 56, 0x08804, 0x0622a, 0x00287 }, { 60, 0x08804, 0x0622b, 0x00287 }, { 64, 0x08804, 0x0622c, 0x00287 }, { 100, 0x08804, 0x02200, 0x00283 }, { 104, 0x08804, 0x02201, 0x00283 }, { 108, 0x08804, 0x02202, 0x00283 }, { 112, 0x08804, 0x02203, 0x00283 }, { 116, 0x08804, 0x02204, 0x00283 }, { 120, 0x08804, 0x02205, 0x00283 }, { 124, 0x08804, 0x02206, 0x00283 }, { 128, 0x08804, 0x02207, 0x00283 }, { 132, 0x08804, 0x02208, 0x00283 }, { 136, 0x08804, 0x02209, 0x00283 }, { 140, 0x08804, 0x0220a, 0x00283 }, { 149, 0x08808, 0x02429, 0x00281 }, { 153, 0x08808, 0x0242b, 0x00281 }, { 157, 0x08808, 0x0242d, 0x00281 }, { 161, 0x08808, 0x0242f, 0x00281 } }; static const struct usbd_config ural_config[URAL_N_TRANSFER] = { [0] = { .type = UE_BULK, .endpoint = -1, /* any */ .direction = UE_DIR_OUT, .bufsize = (MCLBYTES + RAL_TX_DESC_SIZE + 4), .flags = (USBD_USE_DMA|USBD_FORCE_SHORT_XFER), .callback = &ural_bulk_write_callback, }, [1] = { .type = UE_BULK, .endpoint = -1, /* any */ .direction = UE_DIR_IN, .bufsize = (MCLBYTES + RAL_RX_DESC_SIZE), .flags = (USBD_USE_DMA|USBD_SHORT_XFER_OK), .callback = &ural_bulk_read_callback, }, [2] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .callback = &ural_bulk_write_clear_stall_callback, .timeout = 1000, /* 1 second */ }, [3] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .callback = &ural_bulk_read_clear_stall_callback, .timeout = 1000, /* 1 second */ }, }; static devclass_t ural_devclass; static device_method_t ural_methods[] = { DEVMETHOD(device_probe, ural_probe), DEVMETHOD(device_attach, ural_attach), DEVMETHOD(device_detach, ural_detach), { 0, 0 } }; static driver_t ural_driver = { .name = "ural", .methods = ural_methods, .size = sizeof(struct ural_softc), }; DRIVER_MODULE(ural, uhub, ural_driver, ural_devclass, usbd_driver_load, 0); MODULE_DEPEND(ural, usb, 1, 1, 1); MODULE_DEPEND(ural, wlan, 1, 1, 1); static int ural_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); if (uaa->iface != NULL) { return UMATCH_NONE; } return ((usb_lookup(ural_devs, uaa->vendor, uaa->product) != NULL) ? UMATCH_VENDOR_PRODUCT : UMATCH_NONE); } static int ural_attach(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct ural_softc *sc = device_get_softc(dev); int error; if (sc == NULL) { return ENOMEM; } usbd_set_desc(dev, uaa->device); mtx_init(&sc->sc_mtx, "ural lock", MTX_NETWORK_LOCK, MTX_DEF | MTX_RECURSE); snprintf(sc->sc_name, sizeof(sc->sc_name), "%s", device_get_nameunit(dev)); sc->sc_udev = uaa->device; /* XXX */ sc->sc_unit = device_get_unit(dev); __callout_init_mtx(&(sc->sc_watchdog), &(sc->sc_mtx), CALLOUT_RETURNUNLOCKED); error = usbd_set_config_no(uaa->device, RAL_CONFIG_NO, 0); if (error) { device_printf(dev, "could not set configuration " "number, err=%s!\n", usbd_errstr(error)); goto detach; } error = usbd_transfer_setup(uaa->device, RAL_IFACE_INDEX, sc->sc_xfer, ural_config, URAL_N_TRANSFER, sc, &(sc->sc_mtx)); if (error) { device_printf(dev, "could not allocate USB transfers, " "err=%s\n", usbd_errstr(error)) ; goto detach; } error = usbd_config_td_setup(&(sc->sc_config_td), sc, &(sc->sc_mtx), &ural_config_copy, &ural_end_of_commands, sizeof(struct ural_config_copy), 16); if (error) { device_printf(dev, "could not setup config " "thread!\n"); goto detach; } mtx_lock(&(sc->sc_mtx)); /* start setup */ usbd_config_td_queue_command (&(sc->sc_config_td), &ural_cfg_first_time_setup, 0); /* start watchdog (will exit mutex) */ ural_watchdog(sc); return 0; /* success */ detach: ural_detach(dev); return ENXIO; /* failure */ } static int ural_detach(device_t dev) { struct ural_softc *sc = device_get_softc(dev); struct ieee80211com *ic; struct ifnet *ifp; mtx_lock(&(sc->sc_mtx)); usbd_config_td_stop(&(sc->sc_config_td)); __callout_stop(&sc->sc_watchdog); ural_cfg_stop(sc, NULL, 0); ic = &(sc->sc_ic); ifp = ic->ic_ifp; mtx_unlock(&(sc->sc_mtx)); if (ifp) { bpfdetach(ifp); ieee80211_ifdetach(ic); if_free(ifp); } usbd_transfer_unsetup(sc->sc_xfer, URAL_N_TRANSFER); usbd_config_td_unsetup(&(sc->sc_config_td)); __callout_drain(&(sc->sc_watchdog)); mtx_destroy(&sc->sc_mtx); return 0; } /*========================================================================* * REGISTER READ / WRITE WRAPPER ROUTINES *========================================================================*/ static void ural_cfg_do_request(struct ural_softc *sc, usb_device_request_t *req, void *data) { u_int16_t length; usbd_status err; if (usbd_config_td_is_gone(&(sc->sc_config_td))) { goto error; } err = usbd_do_request_flags_mtx(sc->sc_udev, &(sc->sc_mtx), req, data, 0, NULL, 1000); if (err) { printf("%s: device request failed, err=%s " "(ignored)\n", sc->sc_name, usbd_errstr(err)); error: length = UGETW(req->wLength); if ((req->bmRequestType & UT_READ) && length) { bzero(data, length); } } return; } static void ural_cfg_set_testmode(struct ural_softc *sc) { usb_device_request_t req; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = RAL_VENDOR_REQUEST; USETW(req.wValue, 4); USETW(req.wIndex, 1); USETW(req.wLength, 0); ural_cfg_do_request(sc, &req, NULL); return; } static void ural_cfg_eeprom_read(struct ural_softc *sc, u_int16_t addr, void *buf, int len) { usb_device_request_t req; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = RAL_READ_EEPROM; USETW(req.wValue, 0); USETW(req.wIndex, addr); USETW(req.wLength, len); ural_cfg_do_request(sc, &req, buf); return; } static u_int16_t ural_cfg_read(struct ural_softc *sc, u_int16_t reg) { usb_device_request_t req; u_int16_t val; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = RAL_READ_MAC; USETW(req.wValue, 0); USETW(req.wIndex, reg); USETW(req.wLength, sizeof(val)); ural_cfg_do_request(sc, &req, &val); return le16toh(val); } static void ural_cfg_read_multi(struct ural_softc *sc, u_int16_t reg, void *buf, int len) { usb_device_request_t req; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = RAL_READ_MULTI_MAC; USETW(req.wValue, 0); USETW(req.wIndex, reg); USETW(req.wLength, len); ural_cfg_do_request(sc, &req, buf); return; } static void ural_cfg_write(struct ural_softc *sc, u_int16_t reg, u_int16_t val) { usb_device_request_t req; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = RAL_WRITE_MAC; USETW(req.wValue, val); USETW(req.wIndex, reg); USETW(req.wLength, 0); ural_cfg_do_request(sc, &req, NULL); return; } static void ural_cfg_write_multi(struct ural_softc *sc, u_int16_t reg, void *buf, int len) { usb_device_request_t req; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = RAL_WRITE_MULTI_MAC; USETW(req.wValue, 0); USETW(req.wIndex, reg); USETW(req.wLength, len); ural_cfg_do_request(sc, &req, buf); return; } static void ural_cfg_bbp_write(struct ural_softc *sc, u_int8_t reg, u_int8_t val) { u_int16_t tmp; u_int8_t to; for (to = 0; ; to++) { if (to < 5) { tmp = ural_cfg_read(sc, RAL_PHY_CSR8); if (!(tmp & RAL_BBP_BUSY)) { break; } } else { printf("%s: could not write to BBP\n", sc->sc_name); return; } } tmp = (reg << 8) | val; ural_cfg_write(sc, RAL_PHY_CSR7, tmp); return; } static u_int8_t ural_cfg_bbp_read(struct ural_softc *sc, u_int8_t reg) { u_int16_t val; u_int8_t to; val = RAL_BBP_WRITE | (reg << 8); ural_cfg_write(sc, RAL_PHY_CSR7, val); for (to = 0; ; to++) { if (to < 5) { val = ural_cfg_read(sc, RAL_PHY_CSR8); if (!(val & RAL_BBP_BUSY)) { break; } } else { printf("%s: could not read BBP\n", sc->sc_name); return 0; } } return (ural_cfg_read(sc, RAL_PHY_CSR7) & 0xff); } static void ural_cfg_rf_write(struct ural_softc *sc, u_int8_t reg, u_int32_t val) { u_int32_t tmp; u_int8_t to; reg &= 3; for (to = 0; ; to++) { if (to < 5) { tmp = ural_cfg_read(sc, RAL_PHY_CSR10); if (!(tmp & RAL_RF_LOBUSY)) { break; } } else { printf("%s: could not write to RF\n", sc->sc_name); return; } } tmp = RAL_RF_BUSY | RAL_RF_20BIT | ((val & 0xfffff) << 2) | reg; ural_cfg_write(sc, RAL_PHY_CSR9, tmp & 0xffff); ural_cfg_write(sc, RAL_PHY_CSR10, tmp >> 16); /* remember last written value in sc */ sc->sc_rf_regs[reg] = val; DPRINTF(sc, 15, "RF R[%u] <- 0x%05x\n", reg, val & 0xfffff); return; } static void ural_cfg_first_time_setup(struct ural_softc *sc, struct ural_config_copy *cc, u_int16_t refcount) { struct ieee80211com *ic = &(sc->sc_ic); struct ifnet *ifp; register u_int16_t i; if (cc == NULL) { return; } /* retrieve RT2570 rev. no */ sc->sc_asic_rev = ural_cfg_read(sc, RAL_MAC_CSR0); /* retrieve MAC address and various other things from EEPROM */ ural_cfg_read_eeprom(sc); printf("%s: MAC/BBP RT2570 (rev 0x%02x), RF %s\n", sc->sc_name, sc->sc_asic_rev, ural_get_rf(sc->sc_rf_rev)); mtx_unlock(&(sc->sc_mtx)); ifp = if_alloc(IFT_ETHER); mtx_lock(&(sc->sc_mtx)); if (ifp == NULL) { printf("%s: could not if_alloc()!\n", sc->sc_name); goto done; } sc->sc_ifp = ifp; ifp->if_softc = sc; if_initname(ifp, "ural", sc->sc_unit); ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_init = &ural_init_cb; ifp->if_ioctl = &ural_ioctl_cb; ifp->if_start = &ural_start_cb; ifp->if_watchdog = NULL; IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; IFQ_SET_READY(&ifp->if_snd); ic->ic_ifp = ifp; ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */ ic->ic_state = IEEE80211_S_INIT; /* set device capabilities */ ic->ic_caps = IEEE80211_C_IBSS | /* IBSS mode supported */ IEEE80211_C_MONITOR | /* monitor mode supported */ IEEE80211_C_HOSTAP | /* HostAp mode supported */ IEEE80211_C_TXPMGT | /* tx power management */ IEEE80211_C_SHPREAMBLE | /* short preamble supported */ IEEE80211_C_SHSLOT | /* short slot time supported */ IEEE80211_C_WPA; /* 802.11i */ if (sc->sc_rf_rev == RAL_RF_5222) { /* set supported .11a rates */ ic->ic_sup_rates[IEEE80211_MODE_11A] = ural_rateset_11a; /* set supported .11a channels */ for (i = 36; i <= 64; i += 4) { ic->ic_channels[i].ic_freq = ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ); ic->ic_channels[i].ic_flags = IEEE80211_CHAN_A; } for (i = 100; i <= 140; i += 4) { ic->ic_channels[i].ic_freq = ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ); ic->ic_channels[i].ic_flags = IEEE80211_CHAN_A; } for (i = 149; i <= 161; i += 4) { ic->ic_channels[i].ic_freq = ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ); ic->ic_channels[i].ic_flags = IEEE80211_CHAN_A; } } /* set supported .11b and .11g rates */ ic->ic_sup_rates[IEEE80211_MODE_11B] = ural_rateset_11b; ic->ic_sup_rates[IEEE80211_MODE_11G] = ural_rateset_11g; /* set supported .11b and .11g channels (1 through 14) */ for (i = 1; i <= 14; i++) { ic->ic_channels[i].ic_freq = ieee80211_ieee2mhz(i, IEEE80211_CHAN_2GHZ); ic->ic_channels[i].ic_flags = IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM | IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ; } mtx_unlock(&(sc->sc_mtx)); ieee80211_ifattach(ic); mtx_lock(&(sc->sc_mtx)); ic->ic_reset = &ural_reset_cb; /* enable SW bmiss handling in sta mode */ #if (defined(IEEE80211_FEXT_SWBMISS) || (__FreeBSD_version >= 700022)) ic->ic_flags_ext |= IEEE80211_FEXT_SWBMISS; #endif /* override state transition machine */ sc->sc_newstate = ic->ic_newstate; ic->ic_newstate = ural_newstate_cb; mtx_unlock(&(sc->sc_mtx)); ieee80211_media_init(ic, ural_media_change_cb, ieee80211_media_status); bpfattach2(ifp, DLT_IEEE802_11_RADIO, sizeof(struct ieee80211_frame) + 64, &sc->sc_drvbpf); mtx_lock(&(sc->sc_mtx)); sc->sc_rxtap_len = sizeof(sc->sc_rxtap.h); sc->sc_rxtap.h.wr_ihdr.it_len = htole16(sc->sc_rxtap_len); sc->sc_rxtap.h.wr_ihdr.it_present = htole32(RAL_RX_RADIOTAP_PRESENT); sc->sc_txtap_len = sizeof(sc->sc_txtap.h); sc->sc_txtap.h.wt_ihdr.it_len = htole16(sc->sc_txtap_len); sc->sc_txtap.h.wt_ihdr.it_present = htole32(RAL_TX_RADIOTAP_PRESENT); if (bootverbose) { ieee80211_announce(ic); } done: return; } static void ural_end_of_commands(struct ural_softc *sc) { sc->sc_flags &= ~URAL_FLAG_WAIT_COMMAND; if ((sc->sc_flags & URAL_FLAG_LL_READY) && (sc->sc_flags & URAL_FLAG_HL_READY)) { /* start write transfer, if not started */ usbd_transfer_start(sc->sc_xfer[0]); } return; } static void ural_config_copy(struct ural_softc *sc, struct ural_config_copy *cc, u_int16_t refcount) { struct ieee80211com *ic = &(sc->sc_ic); struct ieee80211_channel *c = ic->ic_curchan; struct ifnet *ifp = ic->ic_ifp; u_int8_t n; bzero(cc, sizeof(*cc)); if (c) { cc->ic_curchan.chan_to_ieee = ieee80211_chan2ieee(ic, c); if (c != IEEE80211_CHAN_ANYC) { cc->ic_curchan.chan_is_2ghz = IEEE80211_IS_CHAN_2GHZ(c) ? 1 : 0; } } if (ic->ic_bss) { if ((ic->ic_bss->ni_chan) && (ic->ic_bss->ni_chan != IEEE80211_CHAN_ANYC)) { cc->ic_bss.ni_chan.chan_is_5ghz = IEEE80211_IS_CHAN_5GHZ(ic->ic_bss->ni_chan) ? 1 : 0; } cc->ic_bss.ni_intval = ic->ic_bss->ni_intval; bcopy(ic->ic_bss->ni_bssid, cc->ic_bss.ni_bssid, sizeof(cc->ic_bss.ni_bssid)); } for (n = 0; n < IEEE80211_WEP_NKID; n++) { cc->ic_crypto.cs_nw_keys[n].wk_keyix = ic->ic_crypto.cs_nw_keys[n].wk_keyix; bcopy(ic->ic_crypto.cs_nw_keys[n].wk_key, cc->ic_crypto.cs_nw_keys[n].wk_key, IEEE80211_KEYBUF_SIZE); } cc->ic_opmode = ic->ic_opmode; cc->ic_state = ic->ic_state; cc->ic_flags = ic->ic_flags; if (ifp) { cc->if_flags = ifp->if_flags; } cc->ic_txpowlimit = ic->ic_txpowlimit; cc->ic_curmode = ic->ic_curmode; bcopy(ic->ic_myaddr, cc->ic_myaddr, sizeof(cc->ic_myaddr)); sc->sc_flags |= URAL_FLAG_WAIT_COMMAND; return; } static const char * ural_get_rf(int rev) { switch (rev) { case RAL_RF_2522: return "RT2522"; case RAL_RF_2523: return "RT2523"; case RAL_RF_2524: return "RT2524"; case RAL_RF_2525: return "RT2525"; case RAL_RF_2525E: return "RT2525e"; case RAL_RF_2526: return "RT2526"; case RAL_RF_5222: return "RT5222"; default: return "unknown"; } } /* quickly determine if a given rate is CCK or OFDM */ #define RAL_RATE_IS_OFDM(rate) (((rate) >= 12) && ((rate) != 22)) #define RAL_ACK_SIZE 14 /* 10 + 4(FCS) */ #define RAL_CTS_SIZE 14 /* 10 + 4(FCS) */ #define RAL_SIFS 10 /* us */ #define RAL_RXTX_TURNAROUND 5 /* us */ /*------------------------------------------------------------------------* * ural_rxrate - this function is only used by the Rx radiotap code *------------------------------------------------------------------------*/ static int ural_rxrate(struct ural_rx_desc *desc) { if (le32toh(desc->flags) & RAL_RX_OFDM) { /* reverse function of ural_plcp_signal */ switch (desc->rate) { case 0xb: return 12; case 0xf: return 18; case 0xa: return 24; case 0xe: return 36; case 0x9: return 48; case 0xd: return 72; case 0x8: return 96; case 0xc: return 108; } } else { if (desc->rate == 10) return 2; if (desc->rate == 20) return 4; if (desc->rate == 55) return 11; if (desc->rate == 110) return 22; } return 2; /* should not get there */ } /*------------------------------------------------------------------------* * ural_bulk_read_callback - data read "thread" *------------------------------------------------------------------------*/ static void ural_bulk_read_callback(struct usbd_xfer *xfer) { struct ural_softc *sc = xfer->priv_sc; struct ieee80211com *ic = &(sc->sc_ic); struct ifnet *ifp = ic->ic_ifp; struct ieee80211_node *ni; struct mbuf *m = NULL; u_int32_t flags; u_int32_t max_len; USBD_CHECK_STATUS(xfer); tr_error: if (xfer->error != USBD_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= URAL_FLAG_READ_STALL; usbd_transfer_start(sc->sc_xfer[3]); } return; tr_transferred: if (xfer->actlen < (RAL_RX_DESC_SIZE + IEEE80211_MIN_LEN)) { DPRINTF(sc, 0, "too short transfer, " "%d bytes\n", xfer->actlen); ifp->if_ierrors++; goto tr_setup; } m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); if (m == NULL) { DPRINTF(sc, 0, "could not allocate mbuf\n"); ifp->if_ierrors++; goto tr_setup; } max_len = (xfer->actlen - RAL_RX_DESC_SIZE); usbd_copy_out(&(xfer->buf_data), 0, m->m_data, max_len); usbd_copy_out(&(xfer->buf_data), max_len, &(sc->sc_rx_desc), RAL_RX_DESC_SIZE); flags = le32toh(sc->sc_rx_desc.flags); if ((flags & RAL_RX_PHY_ERROR) || (flags & RAL_RX_CRC_ERROR)) { /* * This should not happen since we did not * request to receive those frames when we * filled RAL_TXRX_CSR2: */ DPRINTF(sc, 5, "PHY or CRC error\n"); ifp->if_ierrors++; goto tr_setup; } /* finalize mbuf */ m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = m->m_len = (flags >> 16) & 0xfff; m->m_flags |= M_HASFCS; /* HW leaves FCS */ if (m->m_len > max_len) { DPRINTF(sc, 0, "invalid length in RX " "descriptor, %u bytes, received %u bytes\n", m->m_len, max_len); ifp->if_ierrors++; goto tr_setup; } if (sc->sc_drvbpf != NULL) { struct ural_rx_radiotap_header *tap = &(sc->sc_rxtap.h); tap->wr_flags = IEEE80211_RADIOTAP_F_FCS; tap->wr_rate = ural_rxrate(&sc->sc_rx_desc); tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq); tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags); tap->wr_antenna = sc->sc_rx_ant; tap->wr_antsignal = sc->sc_rx_desc.rssi; bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_rxtap_len, m); } ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *) (m->m_data)); /* send the frame to the 802.11 layer */ ieee80211_input(ic, m, ni, sc->sc_rx_desc.rssi, 0); /* node is no longer needed */ ieee80211_free_node(ni); DPRINTF(sc, 14, "rx done\n"); m = NULL; tr_setup: if (m) { m_freem(m); } if (sc->sc_flags & URAL_FLAG_READ_STALL) { usbd_transfer_start(sc->sc_xfer[3]); return; } usbd_start_hardware(xfer); return; } static void ural_bulk_read_clear_stall_callback(struct usbd_xfer *xfer) { struct ural_softc *sc = xfer->priv_sc; struct usbd_xfer *xfer_other = sc->sc_xfer[1]; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, xfer_other); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, xfer_other); sc->sc_flags &= ~URAL_FLAG_READ_STALL; usbd_transfer_start(xfer_other); return; tr_error: /* bomb out */ sc->sc_flags &= ~URAL_FLAG_READ_STALL; printf("%s: bulk read pipe stopped\n", sc->sc_name); return; } /*------------------------------------------------------------------------* * ural_ack_rate - return the expected ack rate for a frame * transmitted at rate "rate". * * XXX: this should depend on the destination node basic rate set. *------------------------------------------------------------------------*/ static u_int16_t ural_ack_rate(struct ieee80211com *ic, u_int16_t rate) { switch (rate) { /* CCK rates */ case 2: return 2; case 4: case 11: case 22: return (ic->ic_curmode == IEEE80211_MODE_11B) ? 4 : rate; /* OFDM rates */ case 12: case 18: return 12; case 24: case 36: return 24; case 48: case 72: case 96: case 108: return 48; } /* default to 1Mbps */ return 2; } /*------------------------------------------------------------------------* * ural_txtime - compute the duration (in us) needed to transmit "len" * bytes at rate "rate". The function automatically determines the * operating mode depending on the given rate. `flags' indicates * whether short preamble is in use or not. *------------------------------------------------------------------------*/ static u_int16_t ural_txtime(struct ural_softc *sc, u_int16_t len, u_int16_t rate, u_int32_t flags) { u_int16_t txtime; if (rate < 2) { DPRINTF(sc, 0, "rate < 2!\n"); /* avoid division by zero */ rate = 2; } if (RAL_RATE_IS_OFDM(rate)) { /* IEEE Std 802.11a-1999, pp. 37 */ txtime = (8 + (4 * len) + 3 + rate - 1) / rate; txtime = 16 + 4 + (4 * txtime) + 6; } else { /* IEEE Std 802.11b-1999, pp. 28 */ txtime = ((16 * len) + rate - 1) / rate; if ((rate != 2) && (flags & IEEE80211_F_SHPREAMBLE)) txtime += 72 + 24; else txtime += 144 + 48; } return txtime; } static u_int8_t ural_plcp_signal(u_int16_t rate) { switch (rate) { /* CCK rates (returned values are device-dependent) */ case 2: return 0x0; case 4: return 0x1; case 11: return 0x2; case 22: return 0x3; /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */ case 12: return 0xb; case 18: return 0xf; case 24: return 0xa; case 36: return 0xe; case 48: return 0x9; case 72: return 0xd; case 96: return 0x8; case 108: return 0xc; /* unsupported rates (should not get there) */ default: return 0xff; } } static void ural_setup_tx_desc(struct ural_softc *sc, u_int32_t flags, u_int16_t len, u_int16_t rate) { struct ieee80211com *ic = &(sc->sc_ic); u_int16_t plcp_length; u_int8_t remainder; if (rate < 2) { DPRINTF(sc, 0, "rate < 2!\n"); /* avoid division by zero */ rate = 2; } sc->sc_tx_desc.flags = htole32(flags); sc->sc_tx_desc.flags |= htole32(RAL_TX_NEWSEQ); sc->sc_tx_desc.flags |= htole32(len << 16); sc->sc_tx_desc.wme = htole16(RAL_AIFSN(2) | RAL_LOGCWMIN(3) | RAL_LOGCWMAX(5)); sc->sc_tx_desc.wme |= htole16(RAL_IVOFFSET (sizeof(struct ieee80211_frame))); /* setup PLCP fields */ sc->sc_tx_desc.plcp_signal = ural_plcp_signal(rate); sc->sc_tx_desc.plcp_service = 4; len += IEEE80211_CRC_LEN; if (RAL_RATE_IS_OFDM(rate)) { sc->sc_tx_desc.flags |= htole32(RAL_TX_OFDM); plcp_length = len & 0xfff; sc->sc_tx_desc.plcp_length_hi = plcp_length >> 6; sc->sc_tx_desc.plcp_length_lo = plcp_length & 0x3f; } else { plcp_length = ((16 * len) + rate - 1) / rate; if (rate == 22) { remainder = (16 * len) % 22; if ((remainder != 0) && (remainder < 7)) { sc->sc_tx_desc.plcp_service |= RAL_PLCP_LENGEXT; } } sc->sc_tx_desc.plcp_length_hi = plcp_length >> 8; sc->sc_tx_desc.plcp_length_lo = plcp_length & 0xff; if ((rate != 2) && (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) { sc->sc_tx_desc.plcp_signal |= 0x08; } } sc->sc_tx_desc.iv = 0; sc->sc_tx_desc.eiv = 0; return; } #define RAL_TX_TIMEOUT 5000 /* ms */ /*------------------------------------------------------------------------* * ural_bulk_write_callback - data write "thread" *------------------------------------------------------------------------*/ static void ural_bulk_write_callback(struct usbd_xfer *xfer) { struct ural_softc *sc = xfer->priv_sc; struct ieee80211com *ic = &(sc->sc_ic); struct ifnet *ifp = sc->sc_ic.ic_ifp; struct ieee80211_frame *wh; struct ieee80211_node *ni = NULL; struct ieee80211_key *k; struct ether_header *eh; struct mbuf *m0 = NULL; u_int32_t flags; u_int16_t dur; u_int16_t rate; USBD_CHECK_STATUS(xfer); tr_error: DPRINTF(sc, 10, "transfer error, %s\n", usbd_errstr(xfer->error)); if (xfer->error != USBD_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= URAL_FLAG_WRITE_STALL; usbd_transfer_start(sc->sc_xfer[2]); } ifp->if_oerrors++; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; return; tr_transferred: DPRINTF(sc, 10, "transfer complete\n"); ifp->if_opackets++; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; sc->sc_tx_timer = 0; tr_setup: if (sc->sc_flags & URAL_FLAG_WRITE_STALL) { usbd_transfer_start(sc->sc_xfer[2]); goto out_active; } if (sc->sc_flags & URAL_FLAG_WAIT_COMMAND) { /* don't send anything while a command * is pending ! */ goto done; } if (sc->sc_flags & URAL_FLAG_SEND_BYTE_FRAME) { sc->sc_flags &= ~URAL_FLAG_SEND_BYTE_FRAME; usbd_bzero(&(xfer->buf_data), 0, 1); xfer->length = 1; /* bytes */ xfer->timeout = 1000; /* ms */ usbd_start_hardware(xfer); goto out_active; } if (sc->sc_flags & URAL_FLAG_SEND_BCN_FRAME) { sc->sc_flags &= ~URAL_FLAG_SEND_BCN_FRAME; flags = sc->sc_bcn_flags; rate = sc->sc_bcn_rate; m0 = sc->sc_bcn_mbuf; ural_tx_bcn_complete(sc); goto transmit_frame; } IF_DEQUEUE(&(ic->ic_mgtq), m0); if (m0) { ni = (struct ieee80211_node *)(m0->m_pkthdr.rcvif); m0->m_pkthdr.rcvif = NULL; if (ic->ic_rawbpf != NULL) { bpf_mtap(ic->ic_rawbpf, m0); } flags = 0; rate = (IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan) ? 12 : 2); wh = mtod(m0, struct ieee80211_frame *); if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { flags |= RAL_TX_ACK; dur = ural_txtime(sc, RAL_ACK_SIZE, rate, ic->ic_flags) + RAL_SIFS; *(u_int16_t *)(wh->i_dur) = htole16(dur); /* tell hardware to add timestamp for probe responses */ if (((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_MGT) && ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == IEEE80211_FC0_SUBTYPE_PROBE_RESP)) { flags |= RAL_TX_TIMESTAMP; } } transmit_frame: if (m0->m_pkthdr.len > MCLBYTES) { DPRINTF(sc, 0, "data overflow, %u bytes\n", m0->m_pkthdr.len); m0->m_pkthdr.len = MCLBYTES; } if (sc->sc_drvbpf != NULL) { struct ural_tx_radiotap_header *tap = &(sc->sc_txtap.h); tap->wt_flags = 0; tap->wt_rate = rate; tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq); tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags); tap->wt_antenna = sc->sc_tx_ant; bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_txtap_len, m0); } ural_setup_tx_desc(sc, flags, m0->m_pkthdr.len, rate); usbd_copy_in(&(xfer->buf_data), 0, &(sc->sc_tx_desc), RAL_TX_DESC_SIZE); usbd_m_copy_in(&(xfer->buf_data), RAL_TX_DESC_SIZE, m0, 0, m0->m_pkthdr.len); /* align end on a 2-bytes boundary */ xfer->length = (RAL_TX_DESC_SIZE + m0->m_pkthdr.len + 1) & ~1; /* * No space left in the last URB to store the * extra 2 bytes, force sending of another URB: */ if ((xfer->length % 64) == 0) { xfer->length += 2; } /* zero the extra bytes */ usbd_bzero(&(xfer->buf_data), RAL_TX_DESC_SIZE + m0->m_pkthdr.len, xfer->length - (RAL_TX_DESC_SIZE + m0->m_pkthdr.len)); xfer->timeout = RAL_TX_TIMEOUT; DPRINTF(sc, 10, "sending frame len=%u rate=%u xfer len=%u\n", m0->m_pkthdr.len, rate, xfer->length); m_freem(m0); if (ni) { ieee80211_free_node(ni); } usbd_start_hardware(xfer); goto out_active; } if (ic->ic_state != IEEE80211_S_RUN) { goto done; } IFQ_DRV_DEQUEUE(&ifp->if_snd, m0); if (m0) { if (m0->m_len < sizeof(struct ether_header)) { m0 = m_pullup(m0, sizeof(struct ether_header)); if (m0 == NULL) { goto error; } } eh = mtod(m0, struct ether_header *); ni = ieee80211_find_txnode(ic, eh->ether_dhost); if (ni == NULL) { goto error; } BPF_MTAP(ifp, m0); m0 = ieee80211_encap(ic, m0, ni); if (m0 == NULL) { goto error; } if (ic->ic_rawbpf != NULL) { bpf_mtap(ic->ic_rawbpf, m0); } wh = mtod(m0, struct ieee80211_frame *); flags = 0; rate = ((ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE) ? ic->ic_bss->ni_rates.rs_rates[ic->ic_fixed_rate] : ni->ni_rates.rs_rates[ni->ni_txrate]); rate &= IEEE80211_RATE_VAL; if (wh->i_fc[1] & IEEE80211_FC1_WEP) { k = ieee80211_crypto_encap(ic, ni, m0); if (k == NULL) { goto error; } /* packet header may have moved, reset our local pointer */ wh = mtod(m0, struct ieee80211_frame *); } if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { flags |= RAL_TX_ACK; flags |= RAL_TX_RETRY(7); dur = ural_txtime(sc, RAL_ACK_SIZE, ural_ack_rate(ic, rate), ic->ic_flags) + RAL_SIFS; *(u_int16_t *)(wh->i_dur) = htole16(dur); } goto transmit_frame; } goto done; out_active: ifp->if_drv_flags |= IFF_DRV_OACTIVE; sc->sc_tx_timer = (5*8); done: return; error: if (m0) { m_freem(m0); m0 = NULL; } if (ni) { ieee80211_free_node(ni); ni = NULL; } ifp->if_oerrors++; goto tr_setup; } static void ural_bulk_write_clear_stall_callback(struct usbd_xfer *xfer) { struct ural_softc *sc = xfer->priv_sc; struct usbd_xfer *xfer_other = sc->sc_xfer[0]; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, xfer_other); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, xfer_other); sc->sc_flags &= ~URAL_FLAG_WRITE_STALL; usbd_transfer_start(xfer_other); return; tr_error: /* bomb out */ sc->sc_flags &= ~URAL_FLAG_WRITE_STALL; printf("%s: bulk write pipe stopped\n", sc->sc_name); return; } static void ural_watchdog(void *arg) { struct ural_softc *sc = arg; struct ieee80211com *ic = &(sc->sc_ic); mtx_assert(&(sc->sc_mtx), MA_OWNED); if ((sc->sc_amrr_timer) && (--sc->sc_amrr_timer == 0)) { usbd_config_td_queue_command (&(sc->sc_config_td), &ural_cfg_amrr_timeout, 0); } if ((sc->sc_tx_timer) && (--sc->sc_tx_timer == 0)) { printf("%s: device timeout\n", sc->sc_name); usbd_config_td_queue_command (&(sc->sc_config_td), &ural_cfg_init, 0); } if ((sc->sc_if_timer) && (--sc->sc_if_timer == 0)) { DPRINTF(sc, 0, "watchdog timeout\n"); /* restart timer */ sc->sc_if_timer = (1*8); ieee80211_watchdog(ic); } if ((sc->sc_scan_timer) && (--sc->sc_scan_timer == 0)) { ieee80211_next_scan(ic); } __callout_reset(&(sc->sc_watchdog), hz / 8, &ural_watchdog, sc); mtx_unlock(&(sc->sc_mtx)); return; } /*========================================================================* * IF-net callbacks *========================================================================*/ static void ural_init_cb(void *arg) { struct ural_softc *sc = arg; mtx_lock(&(sc->sc_mtx)); usbd_config_td_queue_command (&(sc->sc_config_td), &ural_cfg_init, 0); mtx_unlock(&(sc->sc_mtx)); return; } static int ural_ioctl_cb(struct ifnet *ifp, u_long cmd, caddr_t data) { struct ural_softc *sc = ifp->if_softc; struct ieee80211com *ic = &(sc->sc_ic); int error = 0; mtx_lock(&(sc->sc_mtx)); switch (cmd) { case SIOCSIFFLAGS: if (ifp->if_flags & IFF_UP) { if (ifp->if_drv_flags & IFF_DRV_RUNNING) { usbd_config_td_queue_command (&(sc->sc_config_td), &ural_cfg_update_promisc, 0); } else { usbd_config_td_queue_command (&(sc->sc_config_td), &ural_cfg_init, 0); } } else { if (ifp->if_drv_flags & IFF_DRV_RUNNING) { usbd_config_td_queue_command (&(sc->sc_config_td), &ural_cfg_stop, 0); } } break; default: error = ieee80211_ioctl(ic, cmd, data); } if (error == ENETRESET) { if ((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING) && (ic->ic_roaming != IEEE80211_ROAMING_MANUAL)) { usbd_config_td_queue_command (&(sc->sc_config_td), &ural_cfg_init, 0); } error = 0; } mtx_unlock(&(sc->sc_mtx)); return error; } static void ural_start_cb(struct ifnet *ifp) { struct ural_softc *sc = ifp->if_softc; mtx_lock(&(sc->sc_mtx)); if ((sc->sc_flags & URAL_FLAG_LL_READY) && (sc->sc_flags & URAL_FLAG_HL_READY)) { /* start write transfer, if not started */ usbd_transfer_start(sc->sc_xfer[0]); } mtx_unlock(&(sc->sc_mtx)); return; } static int ural_media_change_cb(struct ifnet *ifp) { struct ural_softc *sc = ifp->if_softc; int error; mtx_lock(&(sc->sc_mtx)); error = ieee80211_media_change(ifp); if (error != ENETRESET) { goto done; } else { error = 0; } if ((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING)) { usbd_config_td_queue_command (&(sc->sc_config_td), &ural_cfg_init, 0); } done: mtx_unlock(&(sc->sc_mtx)); return error; } /* * This function allows for fast channel switching in monitor mode (used by * net-mgmt/kismet). In IBSS mode, we must explicitly reset the interface to * generate a new beacon frame. */ static int ural_reset_cb(struct ifnet *ifp) { struct ural_softc *sc = ifp->if_softc; struct ieee80211com *ic = &(sc->sc_ic); int error = 0; mtx_lock(&(sc->sc_mtx)); if (ic->ic_opmode != IEEE80211_M_MONITOR) { error = ENETRESET; goto done; } usbd_config_td_queue_command (&(sc->sc_config_td), &ural_cfg_set_chan, 0); done: mtx_unlock(&(sc->sc_mtx)); return 0; } static int ural_newstate_cb(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) { struct ural_softc *sc = ic->ic_ifp->if_softc; mtx_lock(&(sc->sc_mtx)); DPRINTF(sc, 0, "setting new state: %d\n", nstate); /* stop timers */ sc->sc_amrr_timer = 0; sc->sc_scan_timer = 0; sc->sc_if_timer = 8; /* set new state */ switch (nstate) { case IEEE80211_S_INIT: if (sc->sc_ic.ic_state == IEEE80211_S_RUN) { usbd_config_td_queue_command (&(sc->sc_config_td), &ural_cfg_disable_tsf_sync, 0); } sc->sc_if_timer = 0; break; case IEEE80211_S_SCAN: usbd_config_td_queue_command (&(sc->sc_config_td), &ural_cfg_set_chan, 0); sc->sc_scan_timer = 3; break; case IEEE80211_S_AUTH: usbd_config_td_queue_command (&(sc->sc_config_td), &ural_cfg_set_chan, 0); break; case IEEE80211_S_ASSOC: usbd_config_td_queue_command (&(sc->sc_config_td), &ural_cfg_set_chan, 0); break; case IEEE80211_S_RUN: usbd_config_td_queue_command (&(sc->sc_config_td), &ural_cfg_set_run, 0); break; } (sc->sc_newstate)(ic, nstate, arg); mtx_unlock(&(sc->sc_mtx)); return 0; } /*========================================================================* * configure sub-routines, ural_cfg_xxx *========================================================================*/ static void ural_tx_bcn_complete(struct ural_softc *sc) { if (sc->sc_bcn_mbuf) { m_freem(sc->sc_bcn_mbuf); sc->sc_bcn_mbuf = NULL; sc->sc_flags &= ~(URAL_FLAG_SEND_BYTE_FRAME| URAL_FLAG_SEND_BCN_FRAME); } return; } static void ural_cfg_tx_bcn(struct ural_softc *sc) { if ((sc->sc_flags & URAL_FLAG_LL_READY) && (sc->sc_flags & URAL_FLAG_HL_READY)) { struct ieee80211com *ic = &(sc->sc_ic); struct ieee80211_node *ni = sc->sc_ic.ic_bss; struct mbuf *m; if (sc->sc_bcn_mbuf) { DPRINTF(sc,0, "beacon already in progress!\n"); return; } m = ieee80211_beacon_alloc(ic, ni, &sc->sc_bo); if (m == NULL) { printf("%s: could not allocate " "beacon frame\n", sc->sc_name); return; } sc->sc_flags |= (URAL_FLAG_SEND_BYTE_FRAME| URAL_FLAG_SEND_BCN_FRAME); sc->sc_bcn_flags = (RAL_TX_IFS_NEWBACKOFF|RAL_TX_TIMESTAMP); sc->sc_bcn_rate = (IEEE80211_IS_CHAN_5GHZ(ni->ni_chan) ? 12 : 2); sc->sc_bcn_mbuf = m; /* start transfer, if not started */ usbd_transfer_start(sc->sc_xfer[0]); } return; } static void ural_cfg_set_chan(struct ural_softc *sc, struct ural_config_copy *cc, u_int16_t refcount) { u_int32_t i; u_int32_t chan; u_int8_t power; u_int8_t tmp; if (cc == NULL) { /* nothing to do */ return; } chan = cc->ic_curchan.chan_to_ieee; if ((chan == 0) || (chan == IEEE80211_CHAN_ANY)) { /* nothing to do */ return; } if (cc->ic_curchan.chan_is_2ghz) power = min(sc->sc_txpow[chan - 1], 31); else power = 31; /* adjust txpower using ifconfig settings */ power -= (100 - cc->ic_txpowlimit) / 8; DPRINTF(sc, 2, "setting channel to %u, " "tx-power to %u\n", chan, power); switch (sc->sc_rf_rev) { case RAL_RF_2522: ural_cfg_rf_write(sc, RAL_RF1, 0x00814); ural_cfg_rf_write(sc, RAL_RF2, ural_rf2522_r2[chan - 1]); ural_cfg_rf_write(sc, RAL_RF3, (power << 7) | 0x00040); break; case RAL_RF_2523: ural_cfg_rf_write(sc, RAL_RF1, 0x08804); ural_cfg_rf_write(sc, RAL_RF2, ural_rf2523_r2[chan - 1]); ural_cfg_rf_write(sc, RAL_RF3, (power << 7) | 0x38044); ural_cfg_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286); break; case RAL_RF_2524: ural_cfg_rf_write(sc, RAL_RF1, 0x0c808); ural_cfg_rf_write(sc, RAL_RF2, ural_rf2524_r2[chan - 1]); ural_cfg_rf_write(sc, RAL_RF3, (power << 7) | 0x00040); ural_cfg_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286); break; case RAL_RF_2525: ural_cfg_rf_write(sc, RAL_RF1, 0x08808); ural_cfg_rf_write(sc, RAL_RF2, ural_rf2525_hi_r2[chan - 1]); ural_cfg_rf_write(sc, RAL_RF3, (power << 7) | 0x18044); ural_cfg_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286); ural_cfg_rf_write(sc, RAL_RF1, 0x08808); ural_cfg_rf_write(sc, RAL_RF2, ural_rf2525_r2[chan - 1]); ural_cfg_rf_write(sc, RAL_RF3, (power << 7) | 0x18044); ural_cfg_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286); break; case RAL_RF_2525E: ural_cfg_rf_write(sc, RAL_RF1, 0x08808); ural_cfg_rf_write(sc, RAL_RF2, ural_rf2525e_r2[chan - 1]); ural_cfg_rf_write(sc, RAL_RF3, (power << 7) | 0x18044); ural_cfg_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00286 : 0x00282); break; case RAL_RF_2526: ural_cfg_rf_write(sc, RAL_RF2, ural_rf2526_hi_r2[chan - 1]); ural_cfg_rf_write(sc, RAL_RF4, (chan & 1) ? 0x00386 : 0x00381); ural_cfg_rf_write(sc, RAL_RF1, 0x08804); ural_cfg_rf_write(sc, RAL_RF2, ural_rf2526_r2[chan - 1]); ural_cfg_rf_write(sc, RAL_RF3, (power << 7) | 0x18044); ural_cfg_rf_write(sc, RAL_RF4, (chan & 1) ? 0x00386 : 0x00381); break; /* dual-band RF */ case RAL_RF_5222: for (i = 0; i < (sizeof(ural_rf5222)/ sizeof(ural_rf5222[0])); i++) { if (ural_rf5222[i].chan == chan) { ural_cfg_rf_write(sc, RAL_RF1, ural_rf5222[i].r1); ural_cfg_rf_write(sc, RAL_RF2, ural_rf5222[i].r2); ural_cfg_rf_write(sc, RAL_RF3, (power << 7) | 0x00040); ural_cfg_rf_write(sc, RAL_RF4, ural_rf5222[i].r4); break; } } break; } if ((cc->ic_opmode != IEEE80211_M_MONITOR) && (cc->ic_state != IEEE80211_S_SCAN)) { /* set Japan filter bit for channel 14 */ tmp = ural_cfg_bbp_read(sc, 70); if (chan == 14) { tmp |= RAL_JAPAN_FILTER; } else { tmp &= ~RAL_JAPAN_FILTER; } ural_cfg_bbp_write(sc, 70, tmp); /* clear CRC errors */ ural_cfg_read(sc, RAL_STA_CSR0); /* wait a little */ usbd_config_td_sleep(&(sc->sc_config_td), hz/100); ural_cfg_disable_rf_tune(sc); } return; } static void ural_cfg_set_run(struct ural_softc *sc, struct ural_config_copy *cc, u_int16_t refcount) { if (cc == NULL) { /* immediate configuration */ struct ieee80211com *ic = &(sc->sc_ic); /* enable automatic rate adaptation in STA mode */ if ((ic->ic_opmode == IEEE80211_M_STA) && (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE)) { ural_cfg_amrr_start(sc); } return; } /* delayed configuration */ ural_cfg_set_chan(sc, cc, 0); if (cc->ic_opmode != IEEE80211_M_MONITOR) { ural_cfg_update_slot(sc, cc, 0); ural_cfg_set_txpreamble(sc, cc, 0); ural_cfg_set_basicrates(sc, cc, 0); ural_cfg_set_bssid(sc, cc->ic_bss.ni_bssid); } if ((cc->ic_opmode == IEEE80211_M_HOSTAP) || (cc->ic_opmode == IEEE80211_M_IBSS)) { ural_cfg_tx_bcn(sc); } /* make tx led blink on tx (controlled by ASIC) */ ural_cfg_write(sc, RAL_MAC_CSR20, 1); if (cc->ic_opmode != IEEE80211_M_MONITOR) { ural_cfg_enable_tsf_sync(sc, cc , 0); } /* clear statistic registers (STA_CSR0 to STA_CSR10) */ ural_cfg_read_multi(sc, RAL_STA_CSR0, sc->sc_sta, sizeof(sc->sc_sta)); return; } /*------------------------------------------------------------------------* * ural_cfg_disable_rf_tune - disable RF auto-tuning *------------------------------------------------------------------------*/ static void ural_cfg_disable_rf_tune(struct ural_softc *sc) { u_int32_t tmp; if (sc->sc_rf_rev != RAL_RF_2523) { tmp = sc->sc_rf_regs[RAL_RF1] & ~RAL_RF1_AUTOTUNE; ural_cfg_rf_write(sc, RAL_RF1, tmp); } tmp = sc->sc_rf_regs[RAL_RF3] & ~RAL_RF3_AUTOTUNE; ural_cfg_rf_write(sc, RAL_RF3, tmp); DPRINTF(sc, 2, "disabling RF autotune\n"); return; } /*------------------------------------------------------------------------* * ural_cfg_enable_tsf_sync - refer to IEEE Std 802.11-1999 pp. 123 * for more information on TSF synchronization *------------------------------------------------------------------------*/ static void ural_cfg_enable_tsf_sync(struct ural_softc *sc, struct ural_config_copy *cc, u_int16_t refcount) { u_int16_t logcwmin; u_int16_t preload; u_int16_t tmp; if (cc == NULL) { /* nothing to do */ return; } /* first, disable TSF synchronization */ ural_cfg_write(sc, RAL_TXRX_CSR19, 0); tmp = (16 * cc->ic_bss.ni_intval) << 4; ural_cfg_write(sc, RAL_TXRX_CSR18, tmp); logcwmin = (cc->ic_opmode == IEEE80211_M_IBSS) ? 2 : 0; preload = (cc->ic_opmode == IEEE80211_M_IBSS) ? 320 : 6; tmp = (logcwmin << 12) | preload; ural_cfg_write(sc, RAL_TXRX_CSR20, tmp); /* finally, enable TSF synchronization */ tmp = RAL_ENABLE_TSF | RAL_ENABLE_TBCN; if (cc->ic_opmode == IEEE80211_M_STA) tmp |= RAL_ENABLE_TSF_SYNC(1); else tmp |= RAL_ENABLE_TSF_SYNC(2) | RAL_ENABLE_BEACON_GENERATOR; ural_cfg_write(sc, RAL_TXRX_CSR19, tmp); DPRINTF(sc, 0, "enabling TSF synchronization\n"); return; } static void ural_cfg_disable_tsf_sync(struct ural_softc *sc, struct ural_config_copy *cc, u_int16_t refcount) { if (cc == NULL) { /* nothing to do */ return; } /* abort TSF synchronization */ ural_cfg_write(sc, RAL_TXRX_CSR19, 0); /* force tx led to stop blinking */ ural_cfg_write(sc, RAL_MAC_CSR20, 0); return; } static void ural_cfg_update_slot(struct ural_softc *sc, struct ural_config_copy *cc, u_int16_t refcount) { u_int16_t slottime; u_int16_t sifs; u_int16_t eifs; if (cc == NULL) { /* nothing to do */ return; } slottime = (cc->ic_flags & IEEE80211_F_SHSLOT) ? 9 : 20; /* * These settings may sound a bit inconsistent but this is what the * reference driver does. */ if (cc->ic_curmode == IEEE80211_MODE_11B) { sifs = 16 - RAL_RXTX_TURNAROUND; eifs = 364; } else { sifs = 10 - RAL_RXTX_TURNAROUND; eifs = 64; } ural_cfg_write(sc, RAL_MAC_CSR10, slottime); ural_cfg_write(sc, RAL_MAC_CSR11, sifs); ural_cfg_write(sc, RAL_MAC_CSR12, eifs); return; } static void ural_cfg_set_txpreamble(struct ural_softc *sc, struct ural_config_copy *cc, u_int16_t refcount) { u_int16_t tmp; if (cc == NULL) { /* nothing to do */ return; } tmp = ural_cfg_read(sc, RAL_TXRX_CSR10); if (cc->ic_flags & IEEE80211_F_SHPREAMBLE) { tmp |= RAL_SHORT_PREAMBLE; } else { tmp &= ~RAL_SHORT_PREAMBLE; } ural_cfg_write(sc, RAL_TXRX_CSR10, tmp); return; } static void ural_cfg_set_basicrates(struct ural_softc *sc, struct ural_config_copy *cc, u_int16_t refcount) { if (cc == NULL) { /* nothing to do */ return; } /* update basic rate set */ if (cc->ic_curmode == IEEE80211_MODE_11B) { /* 11b basic rates: 1, 2Mbps */ ural_cfg_write(sc, RAL_TXRX_CSR11, 0x3); } else if (cc->ic_bss.ni_chan.chan_is_5ghz) { /* 11a basic rates: 6, 12, 24Mbps */ ural_cfg_write(sc, RAL_TXRX_CSR11, 0x150); } else { /* 11g basic rates: 1, 2, 5.5, 11, 6, 12, 24Mbps */ ural_cfg_write(sc, RAL_TXRX_CSR11, 0x15f); } return; } static void ural_cfg_set_bssid(struct ural_softc *sc, const u_int8_t *bssid) { u_int16_t tmp; tmp = bssid[0] | (bssid[1] << 8); ural_cfg_write(sc, RAL_MAC_CSR5, tmp); tmp = bssid[2] | (bssid[3] << 8); ural_cfg_write(sc, RAL_MAC_CSR6, tmp); tmp = bssid[4] | (bssid[5] << 8); ural_cfg_write(sc, RAL_MAC_CSR7, tmp); DPRINTF(sc, 0, "setting BSSID to 0x%02x%02x%02x%02x%02x%02x\n", bssid[5], bssid[4], bssid[3], bssid[2], bssid[1], bssid[0]); return; } static void ural_cfg_set_macaddr(struct ural_softc *sc, const u_int8_t *addr) { u_int16_t tmp; tmp = addr[0] | (addr[1] << 8); ural_cfg_write(sc, RAL_MAC_CSR2, tmp); tmp = addr[2] | (addr[3] << 8); ural_cfg_write(sc, RAL_MAC_CSR3, tmp); tmp = addr[4] | (addr[5] << 8); ural_cfg_write(sc, RAL_MAC_CSR4, tmp); DPRINTF(sc, 0, "setting MAC to 0x%02x%02x%02x%02x%02x%02x\n", addr[5], addr[4], addr[3], addr[2], addr[1], addr[0]); return; } static void ural_cfg_update_promisc(struct ural_softc *sc, struct ural_config_copy *cc, u_int16_t refcount) { u_int16_t tmp; if (cc == NULL) { /* nothing to do */ return; } tmp = ural_cfg_read(sc, RAL_TXRX_CSR2); if (cc->if_flags & IFF_PROMISC) { tmp &= ~RAL_DROP_NOT_TO_ME; } else { tmp |= RAL_DROP_NOT_TO_ME; } ural_cfg_write(sc, RAL_TXRX_CSR2, tmp); DPRINTF(sc, 0, "%s promiscuous mode\n", (cc->if_flags & IFF_PROMISC) ? "entering" : "leaving"); return; } static void ural_cfg_set_txantenna(struct ural_softc *sc, u_int8_t antenna) { u_int16_t tmp; u_int8_t tx; tx = ural_cfg_bbp_read(sc, RAL_BBP_TX) & ~RAL_BBP_ANTMASK; if (antenna == 1) tx |= RAL_BBP_ANTA; else if (antenna == 2) tx |= RAL_BBP_ANTB; else tx |= RAL_BBP_DIVERSITY; /* need to force I/Q flip for RF 2525e, 2526 and 5222 */ if ((sc->sc_rf_rev == RAL_RF_2525E) || (sc->sc_rf_rev == RAL_RF_2526) || (sc->sc_rf_rev == RAL_RF_5222)) { tx |= RAL_BBP_FLIPIQ; } ural_cfg_bbp_write(sc, RAL_BBP_TX, tx); /* update values in PHY_CSR5 and PHY_CSR6 */ tmp = ural_cfg_read(sc, RAL_PHY_CSR5) & ~0x7; ural_cfg_write(sc, RAL_PHY_CSR5, tmp | (tx & 0x7)); tmp = ural_cfg_read(sc, RAL_PHY_CSR6) & ~0x7; ural_cfg_write(sc, RAL_PHY_CSR6, tmp | (tx & 0x7)); return; } static void ural_cfg_set_rxantenna(struct ural_softc *sc, u_int8_t antenna) { u_int8_t rx; rx = ural_cfg_bbp_read(sc, RAL_BBP_RX) & ~RAL_BBP_ANTMASK; if (antenna == 1) rx |= RAL_BBP_ANTA; else if (antenna == 2) rx |= RAL_BBP_ANTB; else rx |= RAL_BBP_DIVERSITY; /* need to force no I/Q flip for RF 2525e and 2526 */ if ((sc->sc_rf_rev == RAL_RF_2525E) || (sc->sc_rf_rev == RAL_RF_2526)) { rx &= ~RAL_BBP_FLIPIQ; } ural_cfg_bbp_write(sc, RAL_BBP_RX, rx); return; } static void ural_cfg_read_eeprom(struct ural_softc *sc) { struct ieee80211com *ic = &(sc->sc_ic); u_int16_t val; ural_cfg_eeprom_read(sc, RAL_EEPROM_CONFIG0, &val, 2); val = le16toh(val); sc->sc_rf_rev = (val >> 11) & 0x7; sc->sc_hw_radio = (val >> 10) & 0x1; sc->sc_led_mode = (val >> 6) & 0x7; sc->sc_rx_ant = (val >> 4) & 0x3; sc->sc_tx_ant = (val >> 2) & 0x3; sc->sc_nb_ant = (val & 0x3); DPRINTF(sc, 0, "val = 0x%04x\n", val); /* read MAC address */ ural_cfg_eeprom_read(sc, RAL_EEPROM_ADDRESS, ic->ic_myaddr, sizeof(ic->ic_myaddr)); /* read default values for BBP registers */ ural_cfg_eeprom_read(sc, RAL_EEPROM_BBP_BASE, sc->sc_bbp_prom, sizeof(sc->sc_bbp_prom)); /* read Tx power for all b/g channels */ ural_cfg_eeprom_read(sc, RAL_EEPROM_TXPOWER, sc->sc_txpow, sizeof(sc->sc_txpow)); return; } static u_int8_t ural_cfg_bbp_init(struct ural_softc *sc) { u_int16_t i; u_int8_t to; /* wait for BBP to become ready */ for (to = 0; ; to++) { if (to < 10) { if (ural_cfg_bbp_read(sc, RAL_BBP_VERSION) != 0) { break; } if (usbd_config_td_sleep(&(sc->sc_config_td), hz/100)) { break; } } else { printf("%s: timeout waiting for BBP\n", sc->sc_name); return 1; /* failure */ } } /* initialize BBP registers to default values */ for (i = 0; i < (sizeof(ural_def_bbp)/sizeof(ural_def_bbp[0])); i++) { ural_cfg_bbp_write(sc, ural_def_bbp[i].reg, ural_def_bbp[i].val); } #if 0 /* initialize BBP registers to values stored in EEPROM */ for (i = 0; i < 16; i++) { if (sc->sc_bbp_prom[i].reg == 0xff) { continue; } ural_cfg_bbp_write(sc, sc->sc_bbp_prom[i].reg, sc->sc_bbp_prom[i].val); } #endif return 0; /* success */ } static void ural_cfg_init(struct ural_softc *sc, struct ural_config_copy *cc, u_int16_t refcount) { u_int16_t tmp; u_int16_t i; u_int8_t to; if (cc == NULL) { /* immediate configuration */ struct ieee80211com *ic = &(sc->sc_ic); struct ifnet *ifp = sc->sc_ic.ic_ifp; ural_cfg_stop(sc, NULL, 0); ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; ifp->if_drv_flags |= IFF_DRV_RUNNING; sc->sc_flags |= URAL_FLAG_HL_READY; IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp)); if (ic->ic_opmode != IEEE80211_M_MONITOR) { if (ic->ic_roaming != IEEE80211_ROAMING_MANUAL) { ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); } } else { ieee80211_new_state(ic, IEEE80211_S_RUN, -1); } return; } /* delayed configuration */ ural_cfg_set_testmode(sc); ural_cfg_write(sc, 0x308, 0x00f0); /* XXX magic */ ural_cfg_stop(sc, cc, 0); /* initialize MAC registers to default values */ for (i = 0; i < (sizeof(ural_def_mac)/ sizeof(ural_def_mac[0])); i++) { ural_cfg_write(sc, ural_def_mac[i].reg, ural_def_mac[i].val); } /* wait for BBP and RF to wake up (this can take a long time!) */ for (to = 0; ; to++) { if (to < 10) { tmp = ural_cfg_read(sc, RAL_MAC_CSR17); if ((tmp & (RAL_BBP_AWAKE | RAL_RF_AWAKE)) == (RAL_BBP_AWAKE | RAL_RF_AWAKE)) { break; } if (usbd_config_td_sleep(&(sc->sc_config_td), hz/100)) { break; } } else { printf("%s: timeout waiting for " "BBP/RF to wakeup\n", sc->sc_name); goto fail; } } /* we're ready! */ ural_cfg_write(sc, RAL_MAC_CSR1, RAL_HOST_READY); /* set basic rate set (will be updated later) */ ural_cfg_write(sc, RAL_TXRX_CSR11, 0x15f); if (ural_cfg_bbp_init(sc)) { goto fail; } /* set default BSS channel */ ural_cfg_set_chan(sc, cc, 0); /* clear statistic registers (STA_CSR0 to STA_CSR10) */ ural_cfg_read_multi(sc, RAL_STA_CSR0, sc->sc_sta, sizeof(sc->sc_sta)); DPRINTF(sc, 0, "rx_ant=%d, tx_ant=%d\n", sc->sc_rx_ant, sc->sc_tx_ant); ural_cfg_set_txantenna(sc, sc->sc_tx_ant); ural_cfg_set_rxantenna(sc, sc->sc_rx_ant); ural_cfg_set_macaddr(sc, cc->ic_myaddr); /* * Copy WEP keys into adapter's memory (SEC_CSR0 to SEC_CSR31). */ for (i = 0; i < IEEE80211_WEP_NKID; i++) { ural_cfg_write_multi (sc, ((cc->ic_crypto.cs_nw_keys[i].wk_keyix * IEEE80211_KEYBUF_SIZE) + RAL_SEC_CSR0), cc->ic_crypto.cs_nw_keys[i].wk_key, IEEE80211_KEYBUF_SIZE); } /* * make sure that the first transaction * clears the stall: */ sc->sc_flags |= (URAL_FLAG_READ_STALL| URAL_FLAG_WRITE_STALL| URAL_FLAG_LL_READY); if ((sc->sc_flags & URAL_FLAG_LL_READY) && (sc->sc_flags & URAL_FLAG_HL_READY)) { /* start the USB transfers, * if not already started: */ usbd_transfer_start(sc->sc_xfer[1]); usbd_transfer_start(sc->sc_xfer[0]); } /* * start Rx */ tmp = RAL_DROP_PHY | RAL_DROP_CRC; if (cc->ic_opmode != IEEE80211_M_MONITOR) { tmp |= (RAL_DROP_CTL | RAL_DROP_BAD_VERSION); if (cc->ic_opmode != IEEE80211_M_HOSTAP) { tmp |= RAL_DROP_TODS; } if (!(cc->if_flags & IFF_PROMISC)) { tmp |= RAL_DROP_NOT_TO_ME; } } ural_cfg_write(sc, RAL_TXRX_CSR2, tmp); return; fail: ural_cfg_stop(sc, NULL, 0); if (cc) { ural_cfg_stop(sc, cc, 0); } return; } static void ural_cfg_stop(struct ural_softc *sc, struct ural_config_copy *cc, u_int16_t refcount) { if (cc == NULL) { /* immediate configuration */ struct ieee80211com *ic = &(sc->sc_ic); struct ifnet *ifp = ic->ic_ifp; if (ifp) { ieee80211_new_state(ic, IEEE80211_S_INIT, -1); /* clear flags */ ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); } /* stop timers */ sc->sc_tx_timer = 0; sc->sc_if_timer = 0; sc->sc_flags &= ~(URAL_FLAG_HL_READY| URAL_FLAG_LL_READY); /* stop all the transfers, * if not already stopped: */ if (sc->sc_xfer[0]) { usbd_transfer_stop(sc->sc_xfer[0]); } if (sc->sc_xfer[1]) { usbd_transfer_stop(sc->sc_xfer[1]); } if (sc->sc_xfer[2]) { usbd_transfer_stop(sc->sc_xfer[2]); } if (sc->sc_xfer[3]) { usbd_transfer_stop(sc->sc_xfer[3]); } /* stop transmission of * beacon frame, if any: */ ural_tx_bcn_complete(sc); return; } /* delayed configuration */ /* disable Rx */ ural_cfg_write(sc, RAL_TXRX_CSR2, RAL_DISABLE_RX); /* reset ASIC and BBP (but won't reset MAC registers!) */ ural_cfg_write(sc, RAL_MAC_CSR1, RAL_RESET_ASIC | RAL_RESET_BBP); ural_cfg_write(sc, RAL_MAC_CSR1, 0); return; } #define URAL_AMRR_MIN_SUCCESS_THRESHOLD 1 #define URAL_AMRR_MAX_SUCCESS_THRESHOLD 10 static void ural_cfg_amrr_start(struct ural_softc *sc) { struct ieee80211_node *ni = sc->sc_ic.ic_bss; struct ural_amrr *amrr = &sc->sc_amrr; u_int16_t i; amrr->success = 0; amrr->recovery = 0; amrr->txcnt = amrr->retrycnt = 0; amrr->success_threshold = URAL_AMRR_MIN_SUCCESS_THRESHOLD; /* set rate to some reasonable initial value */ i = ni->ni_rates.rs_nrates; while(i) { i--; if ((ni->ni_rates.rs_rates[i] & IEEE80211_RATE_VAL) > 72) break; } ni->ni_txrate = i; sc->sc_amrr_timer = (1*8); return; } static void ural_cfg_amrr_timeout(struct ural_softc *sc, struct ural_config_copy *cc, u_int16_t refcount) { struct ural_amrr *amrr = &sc->sc_amrr; struct ifnet *ifp = sc->sc_ic.ic_ifp; if (cc == NULL) { /* restart timeout */ sc->sc_amrr_timer = (1*8); return; } /* read and clear statistic registers (STA_CSR0 to STA_CSR10) */ ural_cfg_read_multi(sc, RAL_STA_CSR0, sc->sc_sta, sizeof(sc->sc_sta)); if ((sc->sc_flags & URAL_FLAG_LL_READY) && (sc->sc_flags & URAL_FLAG_HL_READY)) { /* count TX retry-fail as Tx errors */ ifp->if_oerrors += sc->sc_sta[9]; amrr->retrycnt = sc->sc_sta[7] + /* TX one-retry ok count */ sc->sc_sta[8] + /* TX more-retry ok count */ sc->sc_sta[9]; /* TX retry-fail count */ amrr->txcnt = amrr->retrycnt + sc->sc_sta[6]; /* TX no-retry ok count */ ural_ratectl(amrr, sc->sc_ic.ic_bss); } return; } /*- * Naive implementation of the Adaptive Multi Rate Retry algorithm: * "IEEE 802.11 Rate Adaptation: A Practical Approach" * Mathieu Lacage, Hossein Manshaei, Thierry Turletti * INRIA Sophia - Projet Planete * http://www-sop.inria.fr/rapports/sophia/RR-5208.html * * This algorithm is particularly well suited for ural since it does not * require per-frame retry statistics. Note however that since h/w does * not provide per-frame stats, we can't do per-node rate adaptation and * thus automatic rate adaptation is only enabled in STA operating mode. */ #define is_success(amrr) \ ((amrr)->retrycnt < ((amrr)->txcnt / 10)) #define is_failure(amrr) \ ((amrr)->retrycnt > ((amrr)->txcnt / 3)) #define is_enough(amrr) \ ((amrr)->txcnt > 10) #define is_min_rate(ni) \ ((ni)->ni_txrate == 0) #define is_max_rate(ni) \ ((ni)->ni_rates.rs_nrates <= 0) || \ ((ni)->ni_txrate == ((ni)->ni_rates.rs_nrates - 1)) #define increase_rate(ni) \ ((ni)->ni_txrate++) #define decrease_rate(ni) \ ((ni)->ni_txrate--) #define reset_cnt(amrr) \ do { (amrr)->txcnt = (amrr)->retrycnt = 0; } while (0) static void ural_ratectl(struct ural_amrr *amrr, struct ieee80211_node *ni) { u_int8_t need_change = 0; if (is_success(amrr) && is_enough(amrr)) { amrr->success++; if ((amrr->success >= amrr->success_threshold) && (!is_max_rate(ni))) { amrr->recovery = 1; amrr->success = 0; increase_rate(ni); need_change = 1; } else { amrr->recovery = 0; } } else if (is_failure(amrr)) { amrr->success = 0; if (!is_min_rate(ni)) { if (amrr->recovery) { amrr->success_threshold *= 2; if (amrr->success_threshold > URAL_AMRR_MAX_SUCCESS_THRESHOLD) { amrr->success_threshold = URAL_AMRR_MAX_SUCCESS_THRESHOLD; } } else { amrr->success_threshold = URAL_AMRR_MIN_SUCCESS_THRESHOLD; } decrease_rate(ni); need_change = 1; } amrr->recovery = 0; /* original paper was incorrect */ } if (is_enough(amrr) || need_change) { reset_cnt(amrr); } return; } pwcbsd/usb/if_zyd.c000644 000423 000000 00000226232 10551670753 015037 0ustar00luigiwheel000000 000000 /* $OpenBSD: if_zyd.c,v 1.27 2006/09/23 22:28:43 mglocker Exp $ */ /* * Copyright (c) 2006 by Florian Stoehr * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include __FBSDID("$FreeBSD: src/sys/dev/usb/if_zyd.c $"); /* * ZyDAS ZD1211 USB WLAN driver * * NOTE: all function names beginning like "zyd_cfg_" can only * be called from within the config thread function ! */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define usbd_config_td_cc zyd_config_copy #define usbd_config_td_softc zyd_softc #include #include #include #include "usbdevs.h" #include #include #ifdef USB_DEBUG #define DPRINTF(sc,n,fmt,...) \ do { if (zyd_debug > (n)) { \ printf("%s:%s: " fmt, (sc)->sc_name, \ __FUNCTION__,## __VA_ARGS__); } } while (0) static int zyd_debug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, zyd, CTLFLAG_RW, 0, "USB zyd"); SYSCTL_INT(_hw_usb_zyd, OID_AUTO, debug, CTLFLAG_RW, &zyd_debug, 0, "zyd debug level"); #else #define DPRINTF(...) #endif static device_probe_t zyd_probe; static device_attach_t zyd_attach; static device_detach_t zyd_detach; static uint16_t zyd_getrealaddr(struct zyd_softc *sc, uint32_t mangled_addr); static void zyd_cfg_usbrequest(struct zyd_softc *sc, uint8_t type, uint8_t request, uint16_t value, uint16_t index, uint16_t length, uint8_t *data); static void zyd_cfg_usbrequestzc(struct zyd_softc *sc, struct zyd_control *zc); static void zyd_cfg_reset(struct zyd_softc *sc); static void zyd_intr_read_clear_stall_callback(struct usbd_xfer *xfer); static void zyd_intr_read_callback(struct usbd_xfer *xfer); static void zyd_cfg_usb_intr_read(struct zyd_softc *sc, void *data, uint32_t size); static void zyd_intr_write_clear_stall_callback(struct usbd_xfer *xfer); static void zyd_intr_write_callback(struct usbd_xfer *xfer); static void zyd_cfg_usb_intr_write(struct zyd_softc *sc, void *data, uint32_t size); static uint32_t zyd_addrinc(uint32_t addr); static void zyd_cfg_read16(struct zyd_softc *sc, uint32_t addr, uint16_t *value); static void zyd_cfg_read32(struct zyd_softc *sc, uint32_t addr, uint32_t *value); static void zyd_cfg_read16_multi(struct zyd_softc *sc, const uint32_t *addrs, uint16_t *data, uint8_t usecount); static void zyd_cfg_read32_multi(struct zyd_softc *sc, const uint32_t *addrs, uint32_t *data, uint8_t usecount); static void zyd_cfg_write16(struct zyd_softc *sc, uint32_t addr, uint16_t value); static void zyd_cfg_write32(struct zyd_softc *sc, uint32_t addr, uint32_t value); static void zyd_cfg_write16_multi(struct zyd_softc *sc, const uint32_t *addrs, uint16_t *data, uint8_t usecount); static void zyd_cfg_write32_multi(struct zyd_softc *sc, const uint32_t *addrs, uint32_t *data, uint8_t usecount); static void zyd_cfg_write16_batch(struct zyd_softc *sc, const struct zyd_adpairs16 *data, uint32_t count); static void zyd_cfg_write32_batch(struct zyd_softc *sc, const struct zyd_adpairs32 *data, uint32_t count); static void zyd_cfg_rfwrite(struct zyd_softc *sc, uint32_t value, uint8_t bits); static void zyd_cfg_stateoutput(struct zyd_softc *sc) __used; static void zyd_rxframeproc(struct usbd_xfer *xfer, uint16_t offset, uint16_t len); static void zyd_bulk_read_clear_stall_callback(struct usbd_xfer *xfer); static void zyd_bulk_read_callback(struct usbd_xfer *xfer); static uint8_t zyd_cfg_uploadfirmware(struct zyd_softc *sc); static void zyd_cfg_lock_phy(struct zyd_softc *sc); static void zyd_cfg_unlock_phy(struct zyd_softc *sc); static void zyd_cfg_get_aw_pt_bi(struct zyd_softc *sc, struct zyd_aw_pt_bi *s); static void zyd_cfg_set_aw_pt_bi(struct zyd_softc *sc, struct zyd_aw_pt_bi *s); static void zyd_cfg_set_beacon_interval(struct zyd_softc *sc, uint32_t interval); static const char *zyd_rf_name(uint8_t type); static void zyd_cfg_read_rf_pa_types(struct zyd_softc *sc, uint8_t *rf_type, uint8_t *pa_type); static void zyd_cfg_rf_rfmd_init(struct zyd_softc *sc, struct zyd_rf *rf); static void zyd_cfg_rf_rfmd_switchradio(struct zyd_softc *sc, uint8_t onoff); static void zyd_cfg_rf_rfmd_set_channel(struct zyd_softc *sc, struct zyd_rf *rf, uint8_t channel); static void zyd_cfg_rf_al2230_switchradio(struct zyd_softc *sc, uint8_t onoff); static void zyd_cfg_rf_al2230_init(struct zyd_softc *sc, struct zyd_rf *rf); static void zyd_cfg_rf_al2230_set_channel(struct zyd_softc *sc, struct zyd_rf *rf, uint8_t channel); static uint8_t zyd_cfg_rf_init_hw(struct zyd_softc *sc, struct zyd_rf *rf, uint8_t type); static uint8_t zyd_cfg_hw_init(struct zyd_softc *sc, struct ieee80211com *ic); static void zyd_cfg_get_e2p_mac_addr(struct zyd_softc *sc, struct zyd_macaddr *mac_addr); static void zyd_cfg_set_mac_addr(struct zyd_softc *sc, const uint8_t *addr); static void zyd_cfg_read_regdomain(struct zyd_softc *sc, uint8_t *regdomain); static uint8_t zyd_regdomain_supported(uint8_t regdomain); static void zyd_cfg_tblreader(struct zyd_softc *sc, uint8_t *values, size_t count, uint32_t e2p_addr, uint32_t guard); static void zyd_cfg_readcaltables(struct zyd_softc *sc); static uint8_t zyd_reset_channel(struct zyd_softc *sc) __used; static void zyd_cfg_set_encryption_type(struct zyd_softc *sc, uint32_t type); static void zyd_cfg_switch_radio(struct zyd_softc *sc, uint8_t onoff); static void zyd_cfg_enable_hwint(struct zyd_softc *sc); static void zyd_cfg_disable_hwint(struct zyd_softc *sc); static void zyd_cfg_set_basic_rates(struct zyd_softc *sc, int mode); static void zyd_cfg_set_mandatory_rates(struct zyd_softc *sc, int mode); static void zyd_cfg_reset_mode(struct zyd_softc *sc); static void zyd_cfg_set_bssid(struct zyd_softc *sc, uint8_t *addr); static void zyd_cfg_first_time_setup(struct zyd_softc *sc, struct zyd_config_copy *cc, uint16_t refcount); static int zyd_media_change_cb(struct ifnet *ifp); static int zyd_newstate_cb(struct ieee80211com *ic, enum ieee80211_state nstate, int arg); static void zyd_cfg_set_run(struct zyd_softc *sc, struct zyd_config_copy *cc, uint16_t refcount); static void zyd_cfg_update_promisc(struct zyd_softc *sc, struct zyd_config_copy *cc, uint16_t refcount); static uint16_t zyd_txtime(uint16_t len, uint8_t rate, uint32_t flags); static uint8_t zyd_plcp_signal(uint8_t rate); static uint16_t zyd_calc_useclen(uint8_t rate, uint16_t len, uint8_t *service); static void zyd_setup_tx_desc(struct usbd_xfer *xfer, struct mbuf *m, uint16_t rate); static void zyd_cfg_dump_fw_registers(struct zyd_softc *sc) __used; static uint8_t zyd_tx_frame(struct usbd_xfer *xfer, struct mbuf *m0, struct ieee80211_node *ni, uint8_t rate); static void zyd_cfg_set_chan(struct zyd_softc *sc, struct zyd_config_copy *cc, u_int16_t refcount); static void zyd_cfg_init_f0(struct zyd_softc *sc, struct zyd_config_copy *cc, u_int16_t refcount); static void zyd_cfg_init_f1(struct zyd_softc *sc, struct zyd_config_copy *cc, u_int16_t refcount); static void zyd_cfg_stop_f0(struct zyd_softc *sc, struct zyd_config_copy *cc, u_int16_t refcount); static void zyd_cfg_stop_f1(struct zyd_softc *sc, struct zyd_config_copy *cc, u_int16_t refcount); static void zyd_start_transfers(struct zyd_softc *sc); static void zyd_start_cb(struct ifnet *ifp); static void zyd_bulk_write_clear_stall_callback(struct usbd_xfer *xfer); static void zyd_bulk_write_callback(struct usbd_xfer *xfer); static void zyd_init_cb(void *arg); static int zyd_reset_cb(struct ifnet *ifp); static int zyd_ioctl_cb(struct ifnet *ifp, u_long command, caddr_t data); static void zyd_watchdog(void *arg); static void zyd_next_scan(void *arg); static void zyd_config_copy(struct zyd_softc *sc, struct zyd_config_copy *cc, u_int16_t refcount); static void zyd_end_of_commands(struct zyd_softc *sc); static const struct usb_devno zyd_devs[] = { { USB_VENDOR_3COM2, USB_PRODUCT_3COM2_3CRUSB10075 }, { USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_WL54 }, { USB_VENDOR_ASUS, USB_PRODUCT_ASUS_WL159G }, { USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D7050C }, { USB_VENDOR_CYBERTAN, USB_PRODUCT_CYBERTAN_TG54USB }, { USB_VENDOR_DRAYTEK, USB_PRODUCT_DRAYTEK_VIGOR550 }, { USB_VENDOR_PLANEX2, USB_PRODUCT_PLANEX2_GWUS54GZL }, { USB_VENDOR_PLANEX3, USB_PRODUCT_PLANEX3_GWUS54MINI }, { USB_VENDOR_SAGEM, USB_PRODUCT_SAGEM_XG760A }, { USB_VENDOR_SITECOMEU, USB_PRODUCT_SITECOMEU_WL113 }, { USB_VENDOR_SWEEX, USB_PRODUCT_SWEEX_ZD1211 }, { USB_VENDOR_TEKRAM, USB_PRODUCT_TEKRAM_QUICKWLAN }, { USB_VENDOR_TEKRAM, USB_PRODUCT_TEKRAM_ZD1211 }, { USB_VENDOR_TWINMOS, USB_PRODUCT_TWINMOS_G240 }, { USB_VENDOR_UMEDIA, USB_PRODUCT_UMEDIA_TEW429UB_A }, { USB_VENDOR_UMEDIA, USB_PRODUCT_UMEDIA_TEW429UB }, { USB_VENDOR_WISTRONNEWEB, USB_PRODUCT_WISTRONNEWEB_UR055G }, { USB_VENDOR_ZYDAS, USB_PRODUCT_ZYDAS_ZD1211 }, { USB_VENDOR_ZYXEL, USB_PRODUCT_ZYXEL_ZYAIRG220 } }; /* Device, regardless of RF */ static const struct zyd_adpairs16 zyd_def_cr[] = { ZYD_DEF_CR }; static const struct zyd_adpairs32 zyd_def_mac[] = { ZYD_DEF_MAC }; /* RF2959 */ static const struct zyd_adpairs16 zyd_rfmd_cr[] = { ZYD_RFMD_CR }; static const uint32_t zyd_rfmd_rf[] = { ZYD_RFMD_RF }; /* AL2230 */ static const struct zyd_adpairs16 zyd_al2230_cr[] = { ZYD_AL2230_CR }; static const uint32_t zyd_al2230_rf[] = { ZYD_AL2230_RF }; static const struct usbd_config zyd_config[ZYD_TR_MAX] = { [ZYD_TR_BULK_DT_WR] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .bufsize = (MCLBYTES + sizeof(struct zyd_controlsetformat) + 1), .flags = (USBD_USE_DMA|USBD_FORCE_SHORT_XFER), .callback = &zyd_bulk_write_callback, .index = 0, .timeout = 10000, /* 10 seconds */ }, [ZYD_TR_BULK_DT_RD] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .bufsize = (MAX(MCLBYTES,2312) + sizeof(struct zyd_rxleninfoapp)), .flags = (USBD_USE_DMA|USBD_SHORT_XFER_OK), .callback = &zyd_bulk_read_callback, .index = 0, }, [ZYD_TR_BULK_CS_WR] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .bufsize = sizeof(usb_device_request_t), .flags = USBD_USE_DMA, .callback = &zyd_bulk_write_clear_stall_callback, .timeout = 1000, /* 1 second */ }, [ZYD_TR_BULK_CS_RD] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .bufsize = sizeof(usb_device_request_t), .flags = USBD_USE_DMA, .callback = &zyd_bulk_read_clear_stall_callback, .timeout = 1000, /* 1 second */ }, [ZYD_TR_INTR_DT_WR] = { .type = UE_BULK_INTR, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .bufsize = ZYD_INTR_BUF_SIZE, .flags = (USBD_USE_DMA|USBD_FORCE_SHORT_XFER), .callback = &zyd_intr_write_callback, .timeout = 1000, /* 1 second */ .index = 1, }, [ZYD_TR_INTR_DT_RD] = { .type = UE_BULK_INTR, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .bufsize = ZYD_INTR_BUF_SIZE, .flags = (USBD_USE_DMA|USBD_SHORT_XFER_OK), .callback = &zyd_intr_read_callback, .timeout = 1000, /* 1 second */ .index = 1, }, [ZYD_TR_INTR_CS_WR] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .bufsize = sizeof(usb_device_request_t), .flags = USBD_USE_DMA, .callback = &zyd_intr_write_clear_stall_callback, .timeout = 1000, /* 1 second */ }, [ZYD_TR_INTR_CS_RD] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .bufsize = sizeof(usb_device_request_t), .flags = USBD_USE_DMA, .callback = &zyd_intr_read_clear_stall_callback, .timeout = 1000, /* 1 second */ }, }; static devclass_t zyd_devclass; static device_method_t zyd_methods[] = { DEVMETHOD(device_probe, zyd_probe), DEVMETHOD(device_attach, zyd_attach), DEVMETHOD(device_detach, zyd_detach), { 0, 0 } }; static driver_t zyd_driver = { .name = "zyd", .methods = zyd_methods, .size = sizeof(struct zyd_softc), }; DRIVER_MODULE(zyd, uhub, zyd_driver, zyd_devclass, usbd_driver_load, 0); MODULE_DEPEND(zyd, usb, 1, 1, 1); MODULE_DEPEND(zyd, wlan, 1, 1, 1); /* * Get the real address from a range-mangled address */ static uint16_t zyd_getrealaddr(struct zyd_softc *sc, uint32_t mangled_addr) { uint16_t add; uint16_t res; add = 0; switch (ZYD_GET_RANGE(mangled_addr)) { case ZYD_RANGE_USB: break; case ZYD_RANGE_CTL: add = ZYD_CTRL_START_ADDR; break; case ZYD_RANGE_E2P: add = ZYD_E2P_START_ADDR; break; case ZYD_RANGE_FW: add = sc->sc_firmware_base; break; } res = (add + ZYD_GET_OFFS(mangled_addr)); return res; } /* * USB request basic wrapper */ static void zyd_cfg_usbrequest(struct zyd_softc *sc, uint8_t type, uint8_t request, uint16_t value, uint16_t index, uint16_t length, uint8_t *data) { usb_device_request_t req; usbd_status err; req.bmRequestType = type; req.bRequest = request; USETW(req.wValue, value); USETW(req.wIndex, index); USETW(req.wLength, length); DPRINTF(sc, 20, "req=%02x val=%02x ind=%02x " "len=%02x\n", request, value, index, length); if (usbd_config_td_is_gone(&(sc->sc_config_td))) { goto error; } err = usbd_do_request_flags_mtx(sc->sc_udev, &(sc->sc_mtx), &req, data, 0, NULL, 10000); if (err) { printf("%s: device request failed, err=%s " "(ignored)\n", sc->sc_name, usbd_errstr(err)); error: length = UGETW(req.wLength); if ((req.bmRequestType & UT_READ) && length) { bzero(data, length); } } return; } /* * Same, higher level */ static void zyd_cfg_usbrequestzc(struct zyd_softc *sc, struct zyd_control *zc) { return zyd_cfg_usbrequest(sc, zc->type, zc->id, zc->value, zc->index, zc->length, zc->data); } /* * Issue a SET_CONFIGURATION command, which will reset the device. */ static void zyd_cfg_reset(struct zyd_softc *sc) { usbd_status err; mtx_unlock(&(sc->sc_mtx)); mtx_lock(&Giant); err = usbreq_set_config(sc->sc_udev, ZYD_CONFIG_NO); mtx_unlock(&Giant); mtx_lock(&(sc->sc_mtx)); if (err) { DPRINTF(sc, 0, "reset failed (ignored)\n"); } /* Wait a little while for the chip to get its brains in order. */ err = usbd_config_td_sleep(&(sc->sc_config_td), hz/10); return; } static void zyd_intr_read_clear_stall_callback(struct usbd_xfer *xfer) { struct zyd_softc *sc = xfer->priv_sc; struct usbd_xfer *xfer_other = sc->sc_xfer[ZYD_TR_INTR_DT_RD]; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, xfer_other); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, xfer_other); sc->sc_flags &= ~ZYD_FLAG_INTR_READ_STALL; usbd_transfer_start(xfer_other); return; tr_error: /* bomb out */ sc->sc_flags &= ~ZYD_FLAG_INTR_READ_STALL; DPRINTF(sc, -1, "clear stall failed\n"); return; } /* * Callback handler for interrupt transfer */ static void zyd_intr_read_callback(struct usbd_xfer *xfer) { struct zyd_softc *sc = xfer->priv_sc; USBD_CHECK_STATUS(xfer); tr_error: DPRINTF(sc, 2, "error=%d\n", xfer->error); if (xfer->error != USBD_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= ZYD_FLAG_INTR_READ_STALL; usbd_transfer_start(sc->sc_xfer[ZYD_TR_INTR_CS_RD]); } goto wakeup; tr_transferred: DPRINTF(sc, 2, "length=%d\n", xfer->actlen); if (xfer->actlen > sizeof(sc->sc_intr_ibuf)) { xfer->actlen = sizeof(sc->sc_intr_ibuf); } usbd_copy_out(&(xfer->buf_data), 0, &(sc->sc_intr_ibuf), xfer->actlen); goto wakeup; tr_setup: if (sc->sc_flags & ZYD_FLAG_INTR_READ_STALL) { usbd_transfer_start(sc->sc_xfer[ZYD_TR_INTR_CS_RD]); goto wakeup; } if (sc->sc_intr_iwakeup) { usbd_start_hardware(xfer); } return; wakeup: sc->sc_intr_iwakeup = 0; wakeup(&(sc->sc_intr_iwakeup)); return; } /* * Interrupt call reply transfer, read */ static void zyd_cfg_usb_intr_read(struct zyd_softc *sc, void *data, uint32_t size) { int error; if (size > sizeof(sc->sc_intr_ibuf)) { DPRINTF(sc, 0, "truncating transfer size!\n"); size = sizeof(sc->sc_intr_ibuf); } if (usbd_config_td_is_gone(&(sc->sc_config_td))) { bzero(data, size); goto done; } sc->sc_intr_iwakeup = 1; bzero(&(sc->sc_intr_ibuf), size); usbd_transfer_start(sc->sc_xfer[ZYD_TR_INTR_DT_RD]); if (sc->sc_intr_iwakeup) { error = msleep(&(sc->sc_intr_iwakeup), &(sc->sc_mtx), 0, "zyd isleep", 0); } bcopy(&(sc->sc_intr_ibuf), data, size); done: return; } static void zyd_intr_write_clear_stall_callback(struct usbd_xfer *xfer) { struct zyd_softc *sc = xfer->priv_sc; struct usbd_xfer *xfer_other = sc->sc_xfer[ZYD_TR_INTR_DT_WR]; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, xfer_other); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, xfer_other); sc->sc_flags &= ~ZYD_FLAG_INTR_WRITE_STALL; usbd_transfer_start(xfer_other); return; tr_error: /* bomb out */ sc->sc_flags &= ~ZYD_FLAG_INTR_WRITE_STALL; return; } static void zyd_intr_write_callback(struct usbd_xfer *xfer) { struct zyd_softc *sc = xfer->priv_sc; USBD_CHECK_STATUS(xfer); tr_error: DPRINTF(sc, 2, "error=%d\n", xfer->error); if (xfer->error != USBD_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= ZYD_FLAG_INTR_WRITE_STALL; usbd_transfer_start(sc->sc_xfer[ZYD_TR_INTR_CS_WR]); } goto wakeup; tr_transferred: DPRINTF(sc, 2, "length=%d\n", xfer->actlen); goto wakeup; tr_setup: if (sc->sc_flags & ZYD_FLAG_INTR_WRITE_STALL) { usbd_transfer_start(sc->sc_xfer[ZYD_TR_INTR_CS_WR]); goto wakeup; } if (sc->sc_intr_owakeup) { usbd_copy_in(&(xfer->buf_data), 0, &(sc->sc_intr_obuf), sc->sc_intr_olen); xfer->length = sc->sc_intr_olen; usbd_start_hardware(xfer); } return; wakeup: sc->sc_intr_owakeup = 0; wakeup(&(sc->sc_intr_owakeup)); return; } /* * Interrupt transfer, write. * * Not always an "interrupt transfer", as if operating in * full speed mode, EP4 is bulk out, not interrupt out. */ static void zyd_cfg_usb_intr_write(struct zyd_softc *sc, void *data, uint32_t size) { int error; if (size > sizeof(sc->sc_intr_obuf)) { DPRINTF(sc, 0, "truncating transfer size!\n"); size = sizeof(sc->sc_intr_obuf); } if (usbd_config_td_is_gone(&(sc->sc_config_td))) { goto done; } sc->sc_intr_olen = size; sc->sc_intr_owakeup = 1; bcopy(data, sc->sc_intr_obuf, size); usbd_transfer_start(sc->sc_xfer[ZYD_TR_INTR_DT_WR]); if (sc->sc_intr_owakeup) { error = msleep(&(sc->sc_intr_owakeup), &(sc->sc_mtx), 0, "zyd osleep", 0); } done: return; } /* * Offset correction (all ranges except CTL use word addressing) */ static uint32_t zyd_addrinc(uint32_t addr) { uint32_t range = ZYD_GET_RANGE(addr); uint32_t offs = ZYD_GET_OFFS(addr); offs += (range == ZYD_RANGE_CTL) ? 2 : 1; return (range | offs); } /* * Read a single 16-bit register */ static void zyd_cfg_read16(struct zyd_softc *sc, uint32_t addr, uint16_t *value) { zyd_cfg_read16_multi(sc, &addr, value, 1); return; } /* * Read a single 32-bit register */ static void zyd_cfg_read32(struct zyd_softc *sc, uint32_t addr, uint32_t *value) { zyd_cfg_read32_multi(sc, &addr, value, 1); return; } /* * Read up to 15 16-bit registers (newer firmware versions) */ static void zyd_cfg_read16_multi(struct zyd_softc *sc, const uint32_t *addrs, uint16_t *data, uint8_t usecount) { struct zyd_intoutmultiread in; struct zyd_intinmultioutput op; uint8_t i; memset(&in, 0, sizeof(struct zyd_intoutmultiread)); memset(&op, 0, sizeof(struct zyd_intinmultioutput)); USETW(in.id, ZYD_CMD_IORDREQ); for (i = 0; i < usecount; i++) USETW(in.addr[i], zyd_getrealaddr(sc, addrs[i])); zyd_cfg_usb_intr_write(sc, &in, (2 + (usecount * 2))); zyd_cfg_usb_intr_read(sc, &op, (2 + (usecount * 4))); for (i = 0; i < usecount; i++) { data[i] = UGETW(op.registers[i].data); } return; } /* * Read up to 7 32-bit registers (newer firmware versions) */ static void zyd_cfg_read32_multi(struct zyd_softc *sc, const uint32_t *addrs, uint32_t *data, uint8_t usecount) { struct zyd_intoutmultiread in; struct zyd_intinmultioutput op; uint8_t i; uint8_t realcount; realcount = usecount * 2; memset(&in, 0, sizeof(struct zyd_intoutmultiread)); memset(&op, 0, sizeof(struct zyd_intinmultioutput)); USETW(in.id, ZYD_CMD_IORDREQ); for (i = 0; i < usecount; i++) { /* high word is first */ USETW(in.addr[i * 2], zyd_getrealaddr(sc, zyd_addrinc(addrs[i]))); USETW(in.addr[(i * 2) + 1], zyd_getrealaddr(sc, addrs[i])); } zyd_cfg_usb_intr_write(sc, &in, (2 + (realcount * 2))); zyd_cfg_usb_intr_read(sc, &op, (2 + (realcount * 4))); for (i = 0; i < usecount; i++) { data[i] = (UGETW(op.registers[i * 2].data) << 16) | UGETW(op.registers[(i * 2) + 1].data); } return; } /* * Write a single 16-bit register */ static void zyd_cfg_write16(struct zyd_softc *sc, uint32_t addr, uint16_t value) { zyd_cfg_write16_multi(sc, &addr, &value, 1); return; } /* * Write a single 32-bit register */ static void zyd_cfg_write32(struct zyd_softc *sc, uint32_t addr, uint32_t value) { zyd_cfg_write32_multi(sc, &addr, &value, 1); return; } /* * Write up to 15 16-bit registers (newer firmware versions) */ static void zyd_cfg_write16_multi(struct zyd_softc *sc, const uint32_t *addrs, uint16_t *data, uint8_t usecount) { struct zyd_intoutmultiwrite mw; uint8_t i; memset(&mw, 0, sizeof(struct zyd_intoutmultiwrite)); USETW(mw.id, ZYD_CMD_IOWRREQ); for (i = 0; i < usecount; i++) { USETW(mw.registers[i].addr, zyd_getrealaddr(sc, addrs[i])); USETW(mw.registers[i].data, data[i]); } zyd_cfg_usb_intr_write(sc, &mw, (2 + (usecount * 4))); return; } /* * Write up to 7 32-bit registers (newer firmware versions) */ static void zyd_cfg_write32_multi(struct zyd_softc *sc, const uint32_t *addrs, uint32_t *data, uint8_t usecount) { struct zyd_intoutmultiwrite mw; uint8_t i; uint8_t realcount; realcount = usecount * 2; memset(&mw, 0, sizeof(struct zyd_intoutmultiwrite)); USETW(mw.id, ZYD_CMD_IOWRREQ); for (i = 0; i < usecount; i++) { /* high word is first */ USETW(mw.registers[i * 2].addr, zyd_getrealaddr(sc, zyd_addrinc(addrs[i]))); USETW(mw.registers[i * 2].data, (*data >> 16)); USETW(mw.registers[(i * 2) + 1].addr, zyd_getrealaddr(sc, addrs[i])); USETW(mw.registers[(i * 2) + 1].data, (*data)); } zyd_cfg_usb_intr_write(sc, &mw, (2 + (realcount * 4))); return; } /* * Batch write 16-bit data */ static void zyd_cfg_write16_batch(struct zyd_softc *sc, const struct zyd_adpairs16 *data, uint32_t count) { /* TODO: Optimize, use multi-writer */ uint32_t i; for (i = 0; i < count; i++) { zyd_cfg_write16(sc, data[i].addr, data[i].data); } return; } /* * Batch write 32-bit data */ static void zyd_cfg_write32_batch(struct zyd_softc *sc, const struct zyd_adpairs32 *data, uint32_t count) { /* TODO: Optimize, use multi-writer */ uint32_t i; for (i = 0; i < count; i++) { zyd_cfg_write32(sc, data[i].addr, data[i].data); } return; } /* * Write RF registers */ static void zyd_cfg_rfwrite(struct zyd_softc *sc, uint32_t value, uint8_t bits) { struct zyd_req_rfwrite req; uint16_t bw_template; uint32_t len; uint8_t i; DPRINTF(sc, 4, "\n"); if (bits > ZYD_REQ_RFWRITE_BITS_MAX) { DPRINTF(sc, 0, "truncating number of bits!\n"); bits = ZYD_REQ_RFWRITE_BITS_MAX; } zyd_cfg_read16(sc, ZYD_CR203, &bw_template); /* Clear template */ bw_template &= ~(ZYD_RF_IF_LE | ZYD_RF_CLK | ZYD_RF_DATA); len = sizeof(req) - sizeof(req.bit_values) + (bits * sizeof(uWord)); USETW(req.id, ZYD_CMD_RFCFGREQ); USETW(req.value, 2); USETW(req.bits, bits); for (i = 0; i < bits; i++) { uint16_t bv = bw_template; if (value & (1 << (bits - 1 - i))) bv |= ZYD_RF_DATA; USETW(req.bit_values[i], bv); } zyd_cfg_usb_intr_write(sc, &req, len); DPRINTF(sc, 4, "wrote %d bits\n", bits); return; } /* * Fetch and print state flags of zydas */ static void zyd_cfg_stateoutput(struct zyd_softc *sc) { uint32_t debug; DPRINTF(sc, 0, "In zyd_stateoutput()\n"); debug = 0; zyd_cfg_read32(sc, ZYD_REG_CTL(0x6D4), &debug); DPRINTF(sc, 0, "DEBUG: Tx complete: %x\n", debug); debug = 0; zyd_cfg_read32(sc, ZYD_REG_CTL(0x6F4), &debug); DPRINTF(sc, 0, "DEBUG: Tx total packet: %x\n", debug); debug = 0; zyd_cfg_read32(sc, ZYD_REG_CTL(0x69C), &debug); DPRINTF(sc, 0, "DEBUG: Rx timeout count: %x\n", debug); debug = 0; zyd_cfg_read32(sc, ZYD_REG_CTL(0x6A0), &debug); DPRINTF(sc, 0, "DEBUG: Rx total frame count: %x\n", debug); debug = 0; zyd_cfg_read32(sc, ZYD_REG_CTL(0x6A4), &debug); DPRINTF(sc, 0, "DEBUG: Rx CRC32: %x\n", debug); debug = 0; zyd_cfg_read32(sc, ZYD_REG_CTL(0x6A8), &debug); DPRINTF(sc, 0, "DEBUG: Rx CRC16: %x\n", debug); debug = 0; zyd_cfg_read32(sc, ZYD_REG_CTL(0x6AC), &debug); DPRINTF(sc, 0, "DEBUG: Rx unicast decr error: %x\n", debug); debug = 0; zyd_cfg_read32(sc, ZYD_REG_CTL(0x6B0), &debug); DPRINTF(sc, 0, "DEBUG: Rx FIFO overrun: %x\n", debug); debug = 0; zyd_cfg_read32(sc, ZYD_REG_CTL(0x6BC), &debug); DPRINTF(sc, 0, "DEBUG: Rx multicast decr error: %x\n", debug); } /* * RX frame processor. * * Needed because rxeof might fetch multiple frames * inside a single USB transfer. */ static void zyd_rxframeproc(struct usbd_xfer *xfer, uint16_t offset, uint16_t len) { struct zyd_softc *sc = xfer->priv_sc; struct ieee80211com *ic = &(sc->sc_ic); struct ifnet *ifp = ic->ic_ifp; struct zyd_rxstatusreport desc; struct ieee80211_node *ni; struct mbuf *m = NULL; /* Too small for at least an RX status report? */ if (len < sizeof(desc)) { DPRINTF(sc, -1, "xfer too short, %d bytes\n", len); ifp->if_ierrors++; goto done; } len -= sizeof(desc); if (len > MCLBYTES) { DPRINTF(sc, 0, "invalid length, %d bytes\n", len); ifp->if_ierrors++; goto done; } m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); if (m == NULL) { DPRINTF(sc, 0, "could not allocate mbuf\n"); ifp->if_ierrors++; goto done; } /* copy out data */ usbd_copy_out(&(xfer->buf_data), offset, m->m_data, len); /* copy out status report */ usbd_copy_out(&(xfer->buf_data), offset + len, &desc, sizeof(desc)); /* * TODO: Signal strength and quality have to be calculated in * conjunction with the PLCP header! The printed values are RAW! */ /* Print RX debug info */ DPRINTF(sc, 3, "Rx status: signalstrength = %d, signalqualitycck = %d, " "signalqualityofdm = %d, decryptiontype = %d, " "modulationtype = %d, rxerrorreason = %d, errorindication = %d\n", desc.signalstrength, desc.signalqualitycck, desc.signalqualityofdm, desc.decryptiontype, desc.modulationtype, desc.rxerrorreason, desc.errorindication); /* Bad frame? */ if (desc.errorindication) { DPRINTF(sc, 0, "RX status indicated error\n"); ifp->if_ierrors++; goto done; } m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = m->m_len = len; m->m_flags |= M_HASFCS; /* hardware appends FCS */ if (sc->sc_drvbpf != NULL) { struct zyd_rx_radiotap_header *tap = &(sc->sc_rxtapu.th); tap->wr_flags = 0; tap->wr_chan_freq = htole16(ic->ic_bss->ni_chan->ic_freq); tap->wr_chan_flags = htole16(ic->ic_bss->ni_chan->ic_flags); tap->wr_rssi = desc.signalstrength; bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_rxtap_len, m); } ni = ieee80211_find_rxnode(ic, (void *)(m->m_data)); /* send the frame to the 802.11 layer */ ieee80211_input(ic, m, ni, desc.signalstrength, 0); /* node is no longer needed */ ieee80211_free_node(ni); DPRINTF(sc, 14, "rx done\n"); m = NULL; done: if (m) { m_freem(m); } return; } static void zyd_bulk_read_clear_stall_callback(struct usbd_xfer *xfer) { struct zyd_softc *sc = xfer->priv_sc; struct usbd_xfer *xfer_other = sc->sc_xfer[ZYD_TR_BULK_DT_RD]; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, xfer_other); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, xfer_other); sc->sc_flags &= ~ZYD_FLAG_BULK_READ_STALL; usbd_transfer_start(xfer_other); return; tr_error: /* bomb out */ sc->sc_flags &= ~ZYD_FLAG_BULK_READ_STALL; return; } static void zyd_bulk_read_callback(struct usbd_xfer *xfer) { struct zyd_softc *sc = xfer->priv_sc; struct ieee80211com *ic = &(sc->sc_ic); struct ifnet *ifp = ic->ic_ifp; struct zyd_rxleninfoapp info; USBD_CHECK_STATUS(xfer); tr_error: DPRINTF(sc, 0, "frame error: %s\n", usbd_errstr(xfer->error)); if (xfer->error != USBD_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= ZYD_FLAG_BULK_READ_STALL; usbd_transfer_start(sc->sc_xfer[ZYD_TR_BULK_CS_RD]); } return; tr_transferred: DPRINTF(sc, 0, "received frame: %d bytes\n", xfer->actlen); /* * It must be at least 4 bytes - still broken if it is * 4 bytes, but that's enough to hold the multi-frame * append header */ if (xfer->actlen < sizeof(info)) { DPRINTF(sc, -1, "xfer too short, %d bytes\n", xfer->actlen); ifp->if_ierrors++; goto tr_setup; } usbd_copy_out(&(xfer->buf_data), xfer->actlen - sizeof(info), &info, sizeof(info)); /* See whether this is a multi-frame tansmission */ if (UGETW(info.marker) == ZYD_MULTIFRAME_MARKER) { /* Multiframe received */ DPRINTF(sc, 3, "Received multi-frame transmission\n"); /* TODO: Support 'em properly */ /* Append PLCP header size */ /* tfs = ZYD_PLCP_HDR_SIZE; for (i = 0; i < 3; ++i) { uint16_t tfl = UGETW(leninfoapp->len[i]); zyd_rxframeproc(data, data->buf + tfs, tfl); tfs += tfl; }*/ } else { DPRINTF(sc, 3, "Received single-frame transmission\n"); if (xfer->actlen < ZYD_PLCP_HDR_SIZE) { goto tr_setup; } zyd_rxframeproc(xfer, ZYD_PLCP_HDR_SIZE, xfer->actlen - ZYD_PLCP_HDR_SIZE); } tr_setup: if (sc->sc_flags & ZYD_FLAG_BULK_READ_STALL) { usbd_transfer_start(sc->sc_xfer[ZYD_TR_BULK_CS_RD]); return; } usbd_start_hardware(xfer); return; } /* * Upload firmware to device. * * Returns nozero on error. * * The whole upload procedure was implemented accordingly to * what ZyDAS' Linux driver does. It does however *NOT* match * what their documentation says (argh...)! */ static uint8_t zyd_cfg_uploadfirmware(struct zyd_softc *sc) { /* ZD1211 uses a proprietary "setup" command to upload the fw */ struct zyd_control zc; uint8_t stsresult; uint16_t imgsize; const uint8_t *imgptr0; uint8_t result = 0; union { const uint8_t *imgptr_const; uint8_t *imgptr; } u; memset(&zc, 0, sizeof(struct zyd_control)); zc.type = ZYD_FIRMDOWN_REQ; zc.id = ZYD_FIRMDOWN_ID; zc.value = ZYD_FIRMWARE_START_ADDR; /* TODO: Different on old ones! */ u.imgptr_const = imgptr0 = zyd_firmware; imgsize = sizeof(zyd_firmware); DPRINTF(sc, 0, "Firmware upload: imgsize=%d\n", imgsize); /* Issue upload command(s) */ while (imgsize > 0) { /* Transfer 4KB max */ uint16_t tlen = (imgsize > 4096) ? 4096 : imgsize; DPRINTF(sc, 0, "Firmware upload: tlen=%d, value=%x\n", tlen, zc.value); zc.length = tlen; zc.data = u.imgptr; zyd_cfg_usbrequestzc(sc, &zc); imgsize -= tlen; u.imgptr += tlen; zc.value += (uint16_t)(tlen / 2); /* Requires word */ } /* See whether the upload succeeded */ memset(&zc, 0, sizeof(struct zyd_control)); zc.type = ZYD_FIRMSTAT_REQ; zc.id = ZYD_FIRMSTAT_ID; zc.value = 0; zc.length = 1; zc.data = &stsresult; zyd_cfg_usbrequestzc(sc, &zc); /* Firmware successfully uploaded? */ if ((stsresult == 0) || (stsresult == ZYD_FIRMWAREUP_FAILURE)) { DPRINTF(sc,-1,"Error: Firmware upload " "failed: 0x%X\n", stsresult); result = 1; } else { DPRINTF(sc, 0, "Firmware successfully " "uploaded\n"); } return result; } /* * Driver OS interface */ /* * Probe for a ZD1211-containing product */ static int zyd_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); if (uaa->iface) return (UMATCH_NONE); return (usb_lookup(zyd_devs, uaa->vendor, uaa->product) != NULL) ? UMATCH_VENDOR_PRODUCT : UMATCH_NONE; } /* * Attach the interface. Allocate softc structures, do * setup and ethernet/BPF attach. */ static int zyd_attach(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct zyd_softc *sc = device_get_softc(dev); int error; if (sc == NULL) { return ENOMEM; } if (uaa->release < ZYD_ALLOWED_DEV_VERSION) { device_printf(dev, "device version mismatch: 0x%X " "(only >= 43.30 supported)\n", uaa->release); return EINVAL; } usbd_set_desc(dev, uaa->device); snprintf(sc->sc_name, sizeof(sc->sc_name), "%s", device_get_nameunit(dev)); sc->sc_unit = device_get_unit(dev); sc->sc_udev = uaa->device; mtx_init(&sc->sc_mtx, "zyd lock", MTX_NETWORK_LOCK, MTX_DEF | MTX_RECURSE); __callout_init_mtx(&(sc->sc_watchdog), &(sc->sc_mtx), CALLOUT_RETURNUNLOCKED); __callout_init_mtx(&(sc->sc_scan_callout), &(sc->sc_mtx), CALLOUT_RETURNUNLOCKED); error = usbd_set_config_no(uaa->device, ZYD_CONFIG_NO, 1); if (error) { device_printf(dev, "setting config no failed: %s\n", usbd_errstr(error)); goto detach; } /* * Endpoint 1 = Bulk out (512b @ high speed / 64b @ full speed) * Endpoint 2 = Bulk in (512b @ high speed / 64b @ full speed) * Endpoint 3 = Intr in (64b) * Endpoint 4 = Intr out @ high speed / bulk out @ full speed (64b) */ error = usbd_transfer_setup(uaa->device, ZYD_IFACE_IDX, sc->sc_xfer, zyd_config, ZYD_TR_MAX, sc, &(sc->sc_mtx)); if (error) { device_printf(dev, "could not allocate USB " "transfers: %s\n", usbd_errstr(error)); goto detach; } error = usbd_config_td_setup(&(sc->sc_config_td), sc, &(sc->sc_mtx), &zyd_config_copy, &zyd_end_of_commands, sizeof(struct zyd_config_copy), 16); if (error) { device_printf(dev, "could not setup config " "thread!\n"); goto detach; } mtx_lock(&(sc->sc_mtx)); /* start setup */ usbd_config_td_queue_command (&(sc->sc_config_td), &zyd_cfg_first_time_setup, 0); /* start watchdog (will exit mutex) */ zyd_watchdog(sc); return 0; detach: zyd_detach(dev); return ENXIO; } /* * Lock PHY registers */ static void zyd_cfg_lock_phy(struct zyd_softc *sc) { uint32_t temp; zyd_cfg_read32(sc, ZYD_MAC_MISC, &temp); temp &= ~ZYD_UNLOCK_PHY_REGS; zyd_cfg_write32(sc, ZYD_MAC_MISC, temp); } /* * Unlock PHY registers */ static void zyd_cfg_unlock_phy(struct zyd_softc *sc) { uint32_t temp; zyd_cfg_read32(sc, ZYD_MAC_MISC, &temp); temp |= ZYD_UNLOCK_PHY_REGS; zyd_cfg_write32(sc, ZYD_MAC_MISC, temp); } /* * Helper beacon (get) */ static void zyd_cfg_get_aw_pt_bi(struct zyd_softc *sc, struct zyd_aw_pt_bi *s) { static const uint32_t addrs[] = { ZYD_CR_ATIM_WND_PERIOD, ZYD_CR_PRE_TBTT, ZYD_CR_BCN_INTERVAL }; uint32_t values[3]; zyd_cfg_read32_multi(sc, addrs, values, 3); s->atim_wnd_period = values[0]; s->pre_tbtt = values[1]; s->beacon_interval = values[2]; DPRINTF(sc, 0, "aw %u pt %u bi %u\n", s->atim_wnd_period, s->pre_tbtt, s->beacon_interval); return; } /* * Helper beacon (set) */ static void zyd_cfg_set_aw_pt_bi(struct zyd_softc *sc, struct zyd_aw_pt_bi *s) { static const uint32_t addrs[] = { ZYD_CR_ATIM_WND_PERIOD, ZYD_CR_PRE_TBTT, ZYD_CR_BCN_INTERVAL }; uint32_t data[3]; if (s->beacon_interval <= 5) s->beacon_interval = 5; if (s->pre_tbtt < 4 || s->pre_tbtt >= s->beacon_interval) s->pre_tbtt = s->beacon_interval - 1; if (s->atim_wnd_period >= s->pre_tbtt) s->atim_wnd_period = s->pre_tbtt - 1; data[0] = s->atim_wnd_period; data[1] = s->pre_tbtt; data[2] = s->beacon_interval; zyd_cfg_write32_multi(sc, addrs, data, 3); return; } /* * Set beacon interval */ static void zyd_cfg_set_beacon_interval(struct zyd_softc *sc, uint32_t interval) { struct zyd_aw_pt_bi s; zyd_cfg_get_aw_pt_bi(sc, &s); s.beacon_interval = interval; zyd_cfg_set_aw_pt_bi(sc, &s); return; } /* * Get RF name */ static const char * zyd_rf_name(uint8_t type) { if (type & 0xf0) type = 0; return zyd_rfs[type]; } /* * Read RF PA types */ static void zyd_cfg_read_rf_pa_types(struct zyd_softc *sc, uint8_t *rf_type, uint8_t *pa_type) { uint32_t value; zyd_cfg_read32(sc, ZYD_E2P_POD, &value); *rf_type = value & 0x0f; *pa_type = (value >> 16) & 0x0f; return; } /* * RF driver: Init for RFMD chip */ static void zyd_cfg_rf_rfmd_init(struct zyd_softc *sc, struct zyd_rf *rf) { uint32_t i; DPRINTF(sc, 0, "ir1 = %d, ir2 = %d\n", (sizeof(zyd_rfmd_cr) / sizeof(struct zyd_adpairs16)), (sizeof(zyd_rfmd_rf) / sizeof(uint32_t))); zyd_cfg_write16_batch(sc, zyd_rfmd_cr, (sizeof(zyd_rfmd_cr) / sizeof(struct zyd_adpairs16))); for (i = 0; i < (sizeof(zyd_rfmd_rf) / sizeof(uint32_t)); i++) { zyd_cfg_rfwrite(sc, zyd_rfmd_rf[i], ZYD_RF_RV_BITS); } return; } /* * RF driver: Switch radio on/off for RFMD chip */ static void zyd_cfg_rf_rfmd_switchradio(struct zyd_softc *sc, uint8_t onoff) { static const struct zyd_adpairs16 ir_on[] = { ZYD_RFMD_RADIO_ON }; static const struct zyd_adpairs16 ir_off[] = { ZYD_RFMD_RADIO_OFF }; if (onoff) { zyd_cfg_write16_batch(sc, ir_on, (sizeof(ir_on) / sizeof(struct zyd_adpairs16))); return; } zyd_cfg_write16_batch(sc, ir_off, (sizeof(ir_off) / sizeof(struct zyd_adpairs16))); return; } /* * RF driver: Channel setting for RFMD chip */ static void zyd_cfg_rf_rfmd_set_channel(struct zyd_softc *sc, struct zyd_rf *rf, uint8_t channel) { static const uint32_t rfmd_table[][2] = { ZYD_RFMD_CHANTABLE }; const uint32_t *dp; uint8_t i; dp = rfmd_table[channel - 1]; for (i = 0; i < 2; i++) { zyd_cfg_rfwrite(sc, dp[i], ZYD_RF_RV_BITS); } return; } /* * RF driver: Switch radio on/off for AL2230 chip */ static void zyd_cfg_rf_al2230_switchradio(struct zyd_softc *sc, uint8_t onoff) { static const struct zyd_adpairs16 ir_on[] = { ZYD_AL2230_RADIO_ON }; static const struct zyd_adpairs16 ir_off[] = { ZYD_AL2230_RADIO_OFF }; if (onoff) { zyd_cfg_write16_batch(sc, ir_on, (sizeof(ir_on) / sizeof(struct zyd_adpairs16))); return; } zyd_cfg_write16_batch(sc, ir_off, (sizeof(ir_off) / sizeof(struct zyd_adpairs16))); return; } /* * RF driver: Init for AL2230 chip */ static void zyd_cfg_rf_al2230_init(struct zyd_softc *sc, struct zyd_rf *rf) { uint32_t i; zyd_cfg_write16_batch(sc, zyd_al2230_cr, (sizeof(zyd_al2230_cr) / sizeof(struct zyd_adpairs16))); for (i = 0; i < (sizeof(zyd_al2230_rf) / sizeof(uint32_t)); i++) { zyd_cfg_rfwrite(sc, zyd_al2230_rf[i], ZYD_RF_RV_BITS); } return; } /* * RF driver: Channel setting for AL2230 chip */ static void zyd_cfg_rf_al2230_set_channel(struct zyd_softc *sc, struct zyd_rf *rf, uint8_t channel) { static const struct zyd_adpairs16 sc_cmd[] = { ZYD_AL2230_SETCHANNEL }; static const uint32_t al2230_table[][3] = { ZYD_AL2230_CHANTABLE }; uint8_t i; const uint32_t *ptr = al2230_table[channel - 1]; for (i = 0; i < 3; i++) { zyd_cfg_rfwrite(sc, *ptr, ZYD_RF_RV_BITS); ptr++; } zyd_cfg_write16_batch(sc, sc_cmd, (sizeof(sc_cmd) / sizeof(struct zyd_adpairs16))); return; } /* * Assign drivers and init the RF */ static uint8_t zyd_cfg_rf_init_hw(struct zyd_softc *sc, struct zyd_rf *rf, uint8_t type) { switch (type) { case ZYD_RF_RFMD: rf->cfg_init_hw = &zyd_cfg_rf_rfmd_init; rf->cfg_switch_radio = &zyd_cfg_rf_rfmd_switchradio; rf->cfg_set_channel = &zyd_cfg_rf_rfmd_set_channel; break; case ZYD_RF_AL2230: rf->cfg_init_hw = &zyd_cfg_rf_al2230_init; rf->cfg_switch_radio = &zyd_cfg_rf_al2230_switchradio; rf->cfg_set_channel = &zyd_cfg_rf_al2230_set_channel; break; default: printf("%s: Sorry, radio %s is not supported yet\n", sc->sc_name, zyd_rf_name(type)); rf->type = 0; return 1; } rf->flags = 0; rf->type = type; zyd_cfg_lock_phy(sc); (rf->cfg_init_hw)(sc, rf); zyd_cfg_unlock_phy(sc); return 0; /* success */ } /* * Init the hardware */ static uint8_t zyd_cfg_hw_init(struct zyd_softc *sc, struct ieee80211com *ic) { uint8_t rf; zyd_cfg_write32(sc, ZYD_MAC_AFTER_PNP, 1); zyd_cfg_read16(sc, ZYD_REG_USB(ZYD_FIRMWARE_BASE_ADDR), &(sc->sc_firmware_base)); DPRINTF(sc, 0, "firmware_base = 0x%04X\n", sc->sc_firmware_base); /* Print the firmware version */ zyd_cfg_read16(sc, ZYD_FW_FIRMWARE_VER, &sc->sc_fw_ver); zyd_cfg_write32(sc, ZYD_CR_GPI_EN, 0); zyd_cfg_write32(sc, ZYD_MAC_CONT_WIN_LIMIT, 0x007f043f); zyd_cfg_set_mandatory_rates(sc, ic->ic_curmode); zyd_cfg_disable_hwint(sc); /* PHY init ("reset") */ zyd_cfg_lock_phy(sc); zyd_cfg_write16_batch(sc, zyd_def_cr, (sizeof(zyd_def_cr) / sizeof(struct zyd_adpairs16))); zyd_cfg_unlock_phy(sc); /* HMAC init */ zyd_cfg_write32_batch(sc, zyd_def_mac, (sizeof(zyd_def_mac) / sizeof(struct zyd_adpairs32))); /* RF/PA types */ zyd_cfg_read_rf_pa_types(sc, &rf, &sc->sc_pa_ver); /* Now init the RF chip */ if (zyd_cfg_rf_init_hw(sc, &sc->sc_rf, rf)) { return 1; /* failure */ } /* Init beacon to 100 * 1024 µs */ zyd_cfg_set_beacon_interval(sc, 100); return 0; /* success */ } /* * Get MAC address from EEPROM */ static void zyd_cfg_get_e2p_mac_addr(struct zyd_softc *sc, struct zyd_macaddr *mac_addr) { uint32_t mac[2]; zyd_cfg_read32(sc, ZYD_E2P_MAC_ADDR_P1, &mac[0]); zyd_cfg_read32(sc, ZYD_E2P_MAC_ADDR_P2, &mac[1]); mac_addr->addr[0] = mac[0]; mac_addr->addr[1] = mac[0] >> 8; mac_addr->addr[2] = mac[0] >> 16; mac_addr->addr[3] = mac[0] >> 24; mac_addr->addr[4] = mac[1]; mac_addr->addr[5] = mac[1] >> 8; return; } /* * Set MAC address (will accept ANY address) */ static void zyd_cfg_set_mac_addr(struct zyd_softc *sc, const uint8_t *addr) { uint32_t addrs[2]; uint32_t trans[2]; addrs[0] = ZYD_MAC_MACADDRL; addrs[1] = ZYD_MAC_MACADDRH; trans[0] = ( (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | (addr[0])); trans[1] = ( (addr[5] << 8) | (addr[4])); zyd_cfg_write32_multi(sc, addrs, trans, 2); return; } /* * Read regdomain */ static void zyd_cfg_read_regdomain(struct zyd_softc *sc, uint8_t *regdomain) { uint32_t value; zyd_cfg_read32(sc, ZYD_E2P_SUBID, &value); *regdomain = value >> 16; return; } /* * Check whether a particular regdomain is supported */ static uint8_t zyd_regdomain_supported(uint8_t regdomain) { const struct zyd_channel_range *range; range = &zyd_channel_ranges[0]; for ( ; ; ) { if (range->regdomain == regdomain) return (range->start != 0); else if (range->regdomain == -1) break; /* end of list */ range++; } return 0; } /* * Helper used by all table readers */ static void zyd_cfg_tblreader(struct zyd_softc *sc, uint8_t *values, size_t count, uint32_t e2p_addr, uint32_t guard) { uint32_t i; uint32_t v; for (i = 0;;) { zyd_cfg_read32(sc, (e2p_addr + (i / 2)), &v); v -= guard; if ((i+4) < count) { values[i++] = v; values[i++] = v >> 8; values[i++] = v >> 16; values[i++] = v >> 24; continue; } for (;i < count; i++) values[i] = v >> (8*(i%3)); return; } return; } /* * Read calibration tables */ static void zyd_cfg_readcaltables(struct zyd_softc *sc) { uint8_t i; static const uint32_t addresses[] = { ZYD_E2P_36M_CAL_VALUE1, ZYD_E2P_48M_CAL_VALUE1, ZYD_E2P_54M_CAL_VALUE1, }; zyd_cfg_tblreader(sc, sc->sc_pwr_cal_values, ZYD_E2P_CHANNEL_COUNT, ZYD_E2P_PWR_CAL_VALUE1, 0); zyd_cfg_tblreader(sc, sc->sc_pwr_int_values, ZYD_E2P_CHANNEL_COUNT, ZYD_E2P_PWR_INT_VALUE1, ZYD_E2P_PWR_INT_GUARD); for (i = 0; i < 3; i++) { zyd_cfg_tblreader(sc, sc->sc_ofdm_cal_values[i], ZYD_E2P_CHANNEL_COUNT, addresses[i], 0); } return; } /* * Reset channel */ static uint8_t zyd_reset_channel(struct zyd_softc *sc) { const struct zyd_channel_range *range; range = &zyd_channel_ranges[0]; for ( ; ; ) { if (range->regdomain == sc->sc_regdomain) if (range->start == 0) return 1; else { sc->sc_channel = range->start; sc->sc_mac_flags &= ~ZMF_FIXED_CHANNEL; } else if (range->regdomain == -1) return 1; /* end of list */ range++; } return 0; } /* * Set encryption type */ static void zyd_cfg_set_encryption_type(struct zyd_softc *sc, uint32_t type) { zyd_cfg_write32(sc, ZYD_MAC_ENCRYPTION_TYPE, type); return; } /* * Switch radio on/off */ static void zyd_cfg_switch_radio(struct zyd_softc *sc, uint8_t onoff) { zyd_cfg_lock_phy(sc); (sc->sc_rf.cfg_switch_radio)(sc, onoff); zyd_cfg_unlock_phy(sc); return; } /* * Enable hardware interrupt */ static void zyd_cfg_enable_hwint(struct zyd_softc *sc) { zyd_cfg_write32(sc, ZYD_CR_INTERRUPT, ZYD_HWINT_ENABLED); return; } /* * Disable hardware interrupt */ static void zyd_cfg_disable_hwint(struct zyd_softc *sc) { zyd_cfg_write32(sc, ZYD_CR_INTERRUPT, ZYD_HWINT_DISABLED); return; } /* * Set basic rates */ static void zyd_cfg_set_basic_rates(struct zyd_softc *sc, int mode) { /* Do not request high rates for the basic set */ uint32_t outf = 0; switch (mode) { case IEEE80211_MODE_11B: /* 11B: 1, 2 MBPS */ outf = 3; break; case IEEE80211_MODE_11G: /* 11G: 6, 12, 24 MBPS */ outf = (21 << 8); break; default: return; } zyd_cfg_write32(sc, ZYD_MAC_BASICRATE, outf); return; } /* * Set mandatory rates. This is the full spectrum of a certain mode. */ static void zyd_cfg_set_mandatory_rates(struct zyd_softc *sc, int mode) { uint32_t outf = 0; switch (mode) { case IEEE80211_MODE_11B: /* 11B: 1, 2, 5.5, 11 */ outf = CSF_RT_CCK_1 | CSF_RT_CCK_2 | CSF_RT_CCK_5_5 | CSF_RT_CCK_11; break; case IEEE80211_MODE_11G: /* 11G: 6, 9, 12, 18, 24, 36, 48, 54 */ outf = CSF_RT_OFDM_6 | CSF_RT_OFDM_9 | CSF_RT_OFDM_12 | CSF_RT_OFDM_18 | CSF_RT_OFDM_24 | CSF_RT_OFDM_36 | CSF_RT_OFDM_48 | CSF_RT_OFDM_54; break; default: return; } zyd_cfg_write32(sc, ZYD_MAC_MANDATORYRATE, outf); return; } /* * Reset mode */ static void zyd_cfg_reset_mode(struct zyd_softc *sc) { static const struct zyd_adpairs32 io[3] = { { ZYD_MAC_STOHOSTSETTING, STH_BCN | STH_PRB_RSP | STH_AUTH | STH_ASS_RSP }, { ZYD_MAC_SNIFFER, 0U }, { ZYD_MAC_ENCRYPTION_TYPE, 0U } }; /* if (ieee->iw_mode == IW_MODE_MONITOR) { ioreqs[0].value = 0xffffffff; ioreqs[1].value = 0x1; ioreqs[2].value = ENC_SNIFFER; }*/ DPRINTF(sc, 0, "\n"); zyd_cfg_write32_batch(sc, io, 3); return; } /* * Set BSSID */ static void zyd_cfg_set_bssid(struct zyd_softc *sc, uint8_t *addr) { uint32_t addrh; uint32_t addrl; addrh = (UGETDW(addr) >> 16); addrl = UGETDW(addr + 2); DPRINTF(sc, 0, "Setting BSSID addrh = %x, addrl = %x\n", addrh, addrl); zyd_cfg_write32(sc, ZYD_MAC_BSSADRL, addrl); zyd_cfg_write32(sc, ZYD_MAC_BSSADRH, addrh); return; } /* * Complete the attach process */ static void zyd_cfg_first_time_setup(struct zyd_softc *sc, struct zyd_config_copy *cc, uint16_t refcount) { struct ieee80211com *ic = &sc->sc_ic; struct ifnet *ifp; struct zyd_macaddr mac; uint32_t i; if (cc == NULL) { return; } if (zyd_cfg_uploadfirmware(sc)) { printf("%s: could not " "upload firmware!\n", sc->sc_name); return; } /* Perform a device reset */ zyd_cfg_reset(sc); /* Init hardware */ if (zyd_cfg_hw_init(sc, ic)) { printf("%s: hw_init() failed!\n", sc->sc_name); return; } /* Read MAC from EEPROM and copy to interface */ zyd_cfg_get_e2p_mac_addr(sc, &mac); memcpy(&sc->sc_ic.ic_myaddr, &mac, IEEE80211_ADDR_LEN); printf("%s: Firmware 0x%04X, Radio %s, PA %#01x, address %02x:%02x:%02x:%02x:%02x:%02x\n", sc->sc_name, sc->sc_fw_ver, zyd_rf_name(sc->sc_rf.type), sc->sc_pa_ver, sc->sc_ic.ic_myaddr[0], sc->sc_ic.ic_myaddr[1], sc->sc_ic.ic_myaddr[2], sc->sc_ic.ic_myaddr[3], sc->sc_ic.ic_myaddr[4], sc->sc_ic.ic_myaddr[5]); /* Read calibration tables from EEPROM */ zyd_cfg_readcaltables(sc); DPRINTF(sc, 0, "Loading regdomain\n"); /* Load the regdomain and see whether it is supported */ zyd_cfg_read_regdomain(sc, &sc->sc_default_regdomain); if (!zyd_regdomain_supported(sc->sc_default_regdomain)) { printf("%s: Error: Regulatory Domain %#04x is not supported.", sc->sc_name, sc->sc_default_regdomain); return; } sc->sc_regdomain = sc->sc_default_regdomain; sc->sc_encrypt = ENC_NOWEP; /* TODO: Is this an allowed channel in the domain? */ sc->sc_channel = ZYD_DEFAULT_CHANNEL; sc->sc_operation = OM_INFRASTRUCTURE; mtx_unlock(&(sc->sc_mtx)); ifp = if_alloc(IFT_ETHER); mtx_lock(&(sc->sc_mtx)); if (ifp == NULL) { printf("%s: could not if_alloc()!\n", sc->sc_name); goto done; } sc->sc_ifp = ifp; ifp->if_softc = sc; if_initname(ifp, "zyd", sc->sc_unit); ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_init = &zyd_init_cb; ifp->if_ioctl = &zyd_ioctl_cb; ifp->if_start = &zyd_start_cb; ifp->if_watchdog = NULL; IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; IFQ_SET_READY(&ifp->if_snd); ifp->if_mtu = ZYD_DEFAULT_MTU; /* Network interface setup */ ic->ic_ifp = ifp; ic->ic_phytype = IEEE80211_T_OFDM; ic->ic_opmode = IEEE80211_M_STA; ic->ic_state = IEEE80211_S_INIT; /* Set device capabilities */ ic->ic_caps = IEEE80211_C_MONITOR | IEEE80211_C_IBSS | IEEE80211_C_HOSTAP | IEEE80211_C_SHPREAMBLE | IEEE80211_C_PMGT | IEEE80211_C_TXPMGT | IEEE80211_C_WEP; /* Rates are in 0,5 MBps units */ ic->ic_sup_rates[IEEE80211_MODE_11B] = zyd_rateset_11b; ic->ic_sup_rates[IEEE80211_MODE_11G] = zyd_rateset_11g; /* set supported .11b and .11g channels (1 through 14) */ for (i = 1; i <= 14; i++) { ic->ic_channels[i].ic_freq = ieee80211_ieee2mhz(i, IEEE80211_CHAN_2GHZ); ic->ic_channels[i].ic_flags = IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM | IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ; } mtx_unlock(&(sc->sc_mtx)); ieee80211_ifattach(ic); mtx_lock(&(sc->sc_mtx)); ic->ic_reset = &zyd_reset_cb; sc->sc_newstate = ic->ic_newstate; ic->ic_newstate = &zyd_newstate_cb; mtx_unlock(&(sc->sc_mtx)); /* setup ifmedia interface */ ieee80211_media_init(ic, &zyd_media_change_cb, &ieee80211_media_status); bpfattach2(ifp, DLT_IEEE802_11_RADIO, sizeof(struct ieee80211_frame) + 64, &sc->sc_drvbpf); mtx_lock(&(sc->sc_mtx)); sc->sc_rxtap_len = sizeof(sc->sc_rxtapu); sc->sc_rxtapu.th.wr_ihdr.it_len = htole16(sc->sc_rxtap_len); sc->sc_rxtapu.th.wr_ihdr.it_present = htole32(ZYD_RX_RADIOTAP_PRESENT); sc->sc_txtap_len = sizeof(sc->sc_txtapu); sc->sc_txtapu.th.wt_ihdr.it_len = htole16(sc->sc_txtap_len); sc->sc_txtapu.th.wt_ihdr.it_present = htole32(ZYD_TX_RADIOTAP_PRESENT); if (bootverbose) { ieee80211_announce(ic); } done: return; } /* * Detach device */ static int zyd_detach(device_t dev) { struct zyd_softc *sc = device_get_softc(dev); struct ieee80211com *ic; struct ifnet *ifp; mtx_lock(&(sc->sc_mtx)); usbd_config_td_stop(&(sc->sc_config_td)); __callout_stop(&(sc->sc_watchdog)); __callout_stop(&(sc->sc_scan_callout)); zyd_cfg_stop_f0(sc, NULL, 0); ic = &(sc->sc_ic); ifp = ic->ic_ifp; mtx_unlock(&(sc->sc_mtx)); if (ifp) { bpfdetach(ifp); ieee80211_ifdetach(ic); if_free(ifp); } usbd_transfer_unsetup(sc->sc_xfer, ZYD_TR_MAX); usbd_config_td_unsetup(&(sc->sc_config_td)); __callout_drain(&(sc->sc_watchdog)); __callout_drain(&(sc->sc_scan_callout)); mtx_destroy(&sc->sc_mtx); return 0; } static int zyd_media_change_cb(struct ifnet *ifp) { struct zyd_softc *sc = ifp->if_softc; int error; mtx_lock(&(sc->sc_mtx)); error = ieee80211_media_change(ifp); if (error != ENETRESET) { goto done; } else { error = 0; } if ((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING)) { usbd_config_td_queue_command (&(sc->sc_config_td), &zyd_cfg_init_f1, 0); } done: mtx_unlock(&(sc->sc_mtx)); return error; } static int zyd_newstate_cb(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) { struct zyd_softc *sc = ic->ic_ifp->if_softc; mtx_lock(&(sc->sc_mtx)); DPRINTF(sc,0, "setting new state %d\n", nstate); /* stop timers */ __callout_stop(&(sc->sc_scan_callout)); if (nstate != IEEE80211_S_INIT) { usbd_config_td_queue_command (&(sc->sc_config_td), &zyd_cfg_set_chan, 0); } switch (nstate) { case IEEE80211_S_INIT: break; case IEEE80211_S_SCAN: __callout_reset(&(sc->sc_scan_callout), hz/5, &zyd_next_scan, sc); break; case IEEE80211_S_AUTH: case IEEE80211_S_ASSOC: break; case IEEE80211_S_RUN: usbd_config_td_queue_command (&(sc->sc_config_td), &zyd_cfg_set_run, 0); break; } sc->sc_newstate(ic, nstate, -1); mtx_unlock(&(sc->sc_mtx)); return 0; } static void zyd_cfg_set_run(struct zyd_softc *sc, struct zyd_config_copy *cc, uint16_t refcount) { if (cc == NULL) { return; } if (cc->ic_opmode != IEEE80211_M_MONITOR) zyd_cfg_set_bssid(sc, cc->ic_bss.ni_bssid); if (cc->ic_opmode == IEEE80211_M_HOSTAP || cc->ic_opmode == IEEE80211_M_IBSS) { sc->sc_flags |= ZYD_FLAG_TX_BEACON; usbd_transfer_start(sc->sc_xfer[ZYD_TR_BULK_DT_WR]); } return; } static void zyd_cfg_update_promisc(struct zyd_softc *sc, struct zyd_config_copy *cc, uint16_t refcount) { if (cc == NULL) { return; } return; } /* * Compute the duration (in us) needed to transmit `len' bytes at rate `rate'. * The function automatically determines the operating mode depending on the * given rate. `flags' indicates whether short preamble is in use or not. */ static uint16_t zyd_txtime(uint16_t len, uint8_t rate, uint32_t flags) { uint16_t txtime; int ceil, dbps; if (rate < 2) { rate = 2; /* avoid division by zero */ } if (ZYD_RATE_IS_OFDM(rate)) { /* * OFDM TXTIME calculation. * From IEEE Std 802.11a-1999, pp. 37. */ dbps = rate * 2; /* data bits per OFDM symbol */ ceil = (16 + (8 * len) + 6) / dbps; if (((16 + (8 * len) + 6) % dbps) != 0) ceil++; txtime = 16 + 4 + (4 * ceil) + 6; } else { /* * High Rate TXTIME calculation. * From IEEE Std 802.11b-1999, pp. 28. */ ceil = (8 * len * 2) / rate; if (((8 * len * 2) % rate) != 0) ceil++; if ((rate != 2) && (flags & IEEE80211_F_SHPREAMBLE)) txtime = 72 + 24 + ceil; else txtime = 144 + 48 + ceil; } return txtime; } /* * Rate-to-bit-converter (Field "rate" in zyd_controlsetformat) */ static uint8_t zyd_plcp_signal(uint8_t rate) { switch (rate) { /* CCK rates */ case 2: return 0x0; case 4: return 0x1; case 11: return 0x2; case 22: return 0x3; /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */ case 12: return 0xb; case 18: return 0xf; case 24: return 0xa; case 36: return 0xe; case 48: return 0x9; case 72: return 0xd; case 96: return 0x8; case 108: return 0xc; /* unsupported rates (should not get there) */ default: return 0xff; } } /* static int zyd_calc_useclen2(uint8_t *service, uint8_t cs_rate, uint16_t tx_length) { static const uint8_t rate_divisor[] = { [ZD_CS_CCK_RATE_1M] = 1, [ZD_CS_CCK_RATE_2M] = 2, [ZD_CS_CCK_RATE_5_5M] = 11, // bits must be doubled [ZD_CS_CCK_RATE_11M] = 11, [ZD_OFDM_RATE_6M] = 6, [ZD_OFDM_RATE_9M] = 9, [ZD_OFDM_RATE_12M] = 12, [ZD_OFDM_RATE_18M] = 18, [ZD_OFDM_RATE_24M] = 24, [ZD_OFDM_RATE_36M] = 36, [ZD_OFDM_RATE_48M] = 48, [ZD_OFDM_RATE_54M] = 54, }; uint32_t bits = (uint32_t)tx_length * 8; uint32_t divisor; divisor = rate_divisor[cs_rate]; if (divisor == 0) return -EINVAL; switch (cs_rate) { case ZD_CS_CCK_RATE_5_5M: bits = (2*bits) + 10; // round up to the next integer break; case ZD_CS_CCK_RATE_11M: if (service) { uint32_t t = bits % 11; *service &= ~ZD_PLCP_SERVICE_LENGTH_EXTENSION; if (0 < t && t <= 3) { *service |= ZD_PLCP_SERVICE_LENGTH_EXTENSION; } } bits += 10; // round up to the next integer break; } return bits/divisor; } enum { R2M_SHORT_PREAMBLE = 0x01, R2M_11A = 0x02, }; */ /* * Calculate frame transmit length in microseconds */ static uint16_t zyd_calc_useclen(uint8_t rate, uint16_t len, uint8_t *service) { uint32_t remainder; uint32_t delta; uint16_t leninus; leninus = 0; *(service) = 0; switch (rate) { case 2: /* 1M bps */ leninus = len << 3; break; case 4: /* 2M bps */ leninus = len << 2; break; case 11: /* 5.5M bps */ leninus = (uint16_t)(((uint32_t)len << 4) / 11); remainder = (((uint32_t)len << 4) % 11); if (remainder) leninus += 1; break; case 22: /* 11M bps */ leninus = (uint16_t)(((uint32_t)len << 3) / 11); remainder = (((uint32_t)len << 3) % 11); delta = 11 - remainder; if (remainder) { leninus += 1; if (delta >= 8) *(service) |= 0x80; /* Bit 7 */ } break; case 12:/* 6M */ leninus = (uint16_t)(((uint32_t)len << 3) / 6); break; case 18:/* 9M */ leninus = (uint16_t)(((uint32_t)len << 3) / 9); break; case 24:/* 12M */ leninus = (uint16_t)(((uint32_t)len << 3) / 12); break; case 36:/* 18M */ leninus = (uint16_t)(((uint32_t)len << 3) / 18); break; case 48:/* 24M */ leninus = (uint16_t)(((uint32_t)len << 3) / 24); break; case 72:/* 36M */ leninus = (uint16_t)(((uint32_t)len << 3) / 36); break; case 96:/* 48M */ leninus = (uint16_t)(((uint32_t)len << 3) / 48); break; case 108: /* 54M */ leninus = (uint16_t)(((uint32_t)len << 3) / 54); break; } return leninus; } /* * Setup the controlsetformat structure */ static void zyd_setup_tx_desc(struct usbd_xfer *xfer, struct mbuf *m, uint16_t rate) { struct zyd_softc *sc = xfer->priv_sc; struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *); struct zyd_controlsetformat desc; uint8_t more_frag = wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG; uint8_t type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; uint8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; uint16_t txlen; uint16_t len = m->m_pkthdr.len; DPRINTF(sc, 0, "enter\n"); bzero(&desc, sizeof(desc)); /* Rate (CCK and OFDM) */ desc.rate = zyd_plcp_signal(rate); /* Modulation type (CCK/OFDM) */ if (ZYD_RATE_IS_OFDM(rate)) desc.modulationtype = CSF_MT_OFDM; else desc.modulationtype = CSF_MT_CCK; /* Preamble/a/g (depending on modtype) */ if (desc.modulationtype == CSF_MT_CCK) { if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) desc.preamble = CSF_PM_CCK_SHORT; } // DEBUG! desc.preamble = 0; /* * Transmit frame length in bytes: * 802.11 MAC header length + raw data length * + ICV/(MIC) length + FCS length. */ txlen = len; /* + 4;*/ desc.txlen = htole16(txlen); /* * If no more fragments, enable backoff protection, * 80211-1999 p. 77 */ if (!more_frag) desc.needbackoff = CSF_BO_RAND; /* Multicast */ if (IEEE80211_IS_MULTICAST(wh->i_addr1)) desc.multicast = CSF_MC_MULTICAST; /* Frame type */ switch (type) { case IEEE80211_FC0_TYPE_DATA: desc.frametype = CSF_FT_DATAFRAME; break; case IEEE80211_FC0_TYPE_MGT: desc.frametype = CSF_FT_MGMTFRAME; break; case IEEE80211_FC0_TYPE_CTL: /* Only subtype PS_POLL has seq control */ if (subtype == IEEE80211_FC0_SUBTYPE_PS_POLL) desc.frametype = CSF_FT_POLLFRAME; else desc.frametype = CSF_FT_NOSEQCONTROL; break; /* All other don't have a sequence control field */ default: desc.frametype = CSF_FT_NOSEQCONTROL; } /* Wake dst. ignored */ /* * RTS/CTS * If the frame is non-multicast, non-mgt, set "RTS" if * fragment size > RTS threshold in CCK mode. In OFDM, set * self cts instead. */ if (!IEEE80211_IS_MULTICAST(wh->i_addr1) && (type != IEEE80211_FC0_TYPE_MGT) && (txlen > ic->ic_rtsthreshold)) { if (ZYD_RATE_IS_OFDM(rate)) desc.selfcts = CSF_SC_SCFRAME; else desc.rts = CSF_RTS_NEEDRTSFRAME; } /* Encryption */ /* * TODO: Hmm ... only set this if hardware performs * encryption. Does it??? */ /* Self cts */ /* if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) desc.selfcts = CSF_SC_SCFRAME;*/ /* Packet length */ desc.packetlength = (sizeof(desc) + len + 1) & ~1; /* Service (PLCP) */ desc.service = 0; /* Current length (usec) */ desc.currentlength = htole16( zyd_calc_useclen(rate, txlen, &desc.service)); /* Next frame length (usec) */ if (more_frag) desc.nextframelen = desc.currentlength; // DEBUG! DPRINTF(sc, 0, "desc: rate=%d, modulationtype=%d, preamble=%d, " "txlen=%d, needbackoff=%d, multicast=%d, frametype=%d, " "wakedst=%d, rts=%d, encryption=%d, selfcts=%d, " "packetlength=%d, currentlength=%d, service=%d, nextframelen=%d\n", desc.rate, desc.modulationtype, desc.preamble, desc.txlen, desc.needbackoff, desc.multicast, desc.frametype, desc.wakedst, desc.rts, desc.encryption, desc.selfcts, desc.packetlength, desc.currentlength, desc.service, desc.nextframelen); usbd_copy_in(&(xfer->buf_data), 0, &desc, sizeof(desc)); return; } static void zyd_cfg_dump_fw_registers(struct zyd_softc *sc) { static const uint32_t addr[4] = { ZYD_FW_FIRMWARE_VER, ZYD_FW_USB_SPEED, ZYD_FW_FIX_TX_RATE, ZYD_FW_LINK_STATUS }; uint32_t i; uint16_t values[4]; for (i = 0; i < 4; ++i) zyd_cfg_read16(sc, addr[i], &values[i]); DPRINTF(sc, 0, "FW_FIRMWARE_VER %#06hx\n", values[0]); DPRINTF(sc, 0, "FW_USB_SPEED %#06hx\n", values[1]); DPRINTF(sc, 0, "FW_FIX_TX_RATE %#06hx\n", values[2]); DPRINTF(sc, 0, "FW_LINK_STATUS %#06hx\n", values[3]); } static uint8_t zyd_tx_frame(struct usbd_xfer *xfer, struct mbuf *m0, struct ieee80211_node *ni, uint8_t rate) { struct zyd_softc *sc = xfer->priv_sc; struct ieee80211com *ic = &(sc->sc_ic); if (m0->m_pkthdr.len > MCLBYTES) { DPRINTF(sc, 0, "data overflow, %u bytes\n", m0->m_pkthdr.len); m0->m_pkthdr.len = MCLBYTES; } if (sc->sc_drvbpf != NULL) { struct zyd_tx_radiotap_header *tap = &(sc->sc_txtapu.th); tap->wt_flags = 0; tap->wt_rate = rate; tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq); tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags); bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_txtap_len, m0); } zyd_setup_tx_desc(xfer, m0, rate); usbd_m_copy_in(&(xfer->buf_data), sizeof(struct zyd_controlsetformat), m0, 0, m0->m_pkthdr.len); xfer->length = (sizeof(struct zyd_controlsetformat) + m0->m_pkthdr.len); /* xfer length needs to be a multiple of two! */ if (xfer->length & 1) { usbd_bzero(&(xfer->buf_data), xfer->length, 1); xfer->length ++; } DPRINTF(sc, 0, "len=%u rate=%u xfer len=%u\n", m0->m_pkthdr.len, rate, xfer->length); usbd_start_hardware(xfer); m_freem(m0); if (ni) { ieee80211_free_node(ni); } return 0; /* success */ } static void zyd_cfg_set_chan(struct zyd_softc *sc, struct zyd_config_copy *cc, u_int16_t refcount) { uint32_t chan; if (cc == NULL) { return; } chan = cc->ic_curchan.chan_to_ieee; DPRINTF(sc, 0, "Will try %d\n", chan); if (chan == 0 || chan == IEEE80211_CHAN_ANY) { DPRINTF(sc, 0, "0 or ANY, exiting\n"); return; } zyd_cfg_lock_phy(sc); (sc->sc_rf.cfg_set_channel)(sc, &sc->sc_rf, chan); /* Power integration */ zyd_cfg_write32(sc, ZYD_CR31, sc->sc_pwr_int_values[chan - 1]); /* Power calibration */ zyd_cfg_write32(sc, ZYD_CR68, sc->sc_pwr_cal_values[chan - 1]); zyd_cfg_unlock_phy(sc); } /* * Interface: init */ /* immediate configuration */ static void zyd_cfg_init_f0(struct zyd_softc *sc, struct zyd_config_copy *cc, u_int16_t refcount) { struct ieee80211com *ic = &(sc->sc_ic); struct ifnet *ifp = sc->sc_ic.ic_ifp; zyd_cfg_stop_f0(sc, cc, 0); ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; ifp->if_drv_flags |= IFF_DRV_RUNNING; sc->sc_flags |= ZYD_FLAG_HL_READY; IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp)); if (ic->ic_opmode != IEEE80211_M_MONITOR) { if (ic->ic_roaming != IEEE80211_ROAMING_MANUAL) { ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); } } else { ieee80211_new_state(ic, IEEE80211_S_RUN, -1); } return; } /* delayed configuration */ static void zyd_cfg_init_f1(struct zyd_softc *sc, struct zyd_config_copy *cc, u_int16_t refcount) { uint32_t statedata; if (cc == NULL) { zyd_cfg_init_f0(sc, cc, refcount); return; } zyd_cfg_stop_f1(sc, cc, 0); /* Do initial setup */ zyd_cfg_set_mac_addr(sc, cc->ic_myaddr); /* DPRINTF(sc, 0, "Reset channel\n"); if (zyd_reset_channel(sc) != 0) { return USBD_INVAL; }*/ zyd_cfg_set_encryption_type(sc, sc->sc_encrypt); /* TODO: Check what we've already initialized in the hw_init section */ /* Additional init */ zyd_cfg_reset_mode(sc); zyd_cfg_switch_radio(sc, 1); /* Set basic rates */ zyd_cfg_set_basic_rates(sc, cc->ic_curmode); /* Set mandatory rates */ /* zyd_cfg_set_mandatory_rates(sc, cc->ic_curmode); */ /* set default BSS channel */ zyd_cfg_set_chan(sc, cc, 0); zyd_cfg_enable_hwint(sc); /* Load the multicast filter. */ /*zyd_setmulti(sc); */ zyd_cfg_read32(sc, ZYD_REG_CTL(0x684), &statedata); return; } /* immediate configuration */ static void zyd_cfg_stop_f0(struct zyd_softc *sc, struct zyd_config_copy *cc, u_int16_t refcount) { struct ieee80211com *ic = &(sc->sc_ic); struct ifnet *ifp = ic->ic_ifp; if (ifp) { ieee80211_new_state(ic, IEEE80211_S_INIT, -1); /* clear flags */ ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); } sc->sc_flags &= ~(ZYD_FLAG_HL_READY| ZYD_FLAG_LL_READY| ZYD_FLAG_TX_BEACON); /* stop all the transfers, * if not already stopped: */ if (sc->sc_xfer[ZYD_TR_BULK_DT_WR]) { usbd_transfer_stop(sc->sc_xfer[ZYD_TR_BULK_DT_WR]); } if (sc->sc_xfer[ZYD_TR_BULK_DT_RD]) { usbd_transfer_stop(sc->sc_xfer[ZYD_TR_BULK_DT_RD]); } if (sc->sc_xfer[ZYD_TR_BULK_CS_WR]) { usbd_transfer_stop(sc->sc_xfer[ZYD_TR_BULK_CS_WR]); } if (sc->sc_xfer[ZYD_TR_BULK_CS_RD]) { usbd_transfer_stop(sc->sc_xfer[ZYD_TR_BULK_CS_RD]); } return; } /* delayed configuration */ static void zyd_cfg_stop_f1(struct zyd_softc *sc, struct zyd_config_copy *cc, u_int16_t refcount) { if (cc == NULL) { zyd_cfg_stop_f0(sc, cc, refcount); return; } zyd_cfg_reset(sc); return; } static void zyd_start_transfers(struct zyd_softc *sc) { if ((sc->sc_flags & ZYD_FLAG_LL_READY) && (sc->sc_flags & ZYD_FLAG_HL_READY)) { /* start the USB transfers, * if not already started: */ usbd_transfer_start(sc->sc_xfer[ZYD_TR_BULK_DT_WR]); usbd_transfer_start(sc->sc_xfer[ZYD_TR_BULK_DT_RD]); } return; } static void zyd_start_cb(struct ifnet *ifp) { struct zyd_softc *sc = ifp->if_softc; mtx_lock(&(sc->sc_mtx)); zyd_start_transfers(sc); mtx_unlock(&(sc->sc_mtx)); return; } static void zyd_bulk_write_clear_stall_callback(struct usbd_xfer *xfer) { struct zyd_softc *sc = xfer->priv_sc; struct usbd_xfer *xfer_other = sc->sc_xfer[ZYD_TR_BULK_DT_WR]; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, xfer_other); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, xfer_other); sc->sc_flags &= ~ZYD_FLAG_BULK_WRITE_STALL; usbd_transfer_start(xfer_other); return; tr_error: /* bomb out */ sc->sc_flags &= ~ZYD_FLAG_BULK_WRITE_STALL; return; } static void zyd_bulk_write_callback(struct usbd_xfer *xfer) { struct zyd_softc *sc = xfer->priv_sc; struct ieee80211com *ic = &(sc->sc_ic); struct ifnet *ifp = sc->sc_ic.ic_ifp; struct ieee80211_frame *wh; struct ieee80211_node *ni = NULL; struct ieee80211_rateset *rs; /* struct ieee80211_key *k; */ struct ether_header *eh; struct mbuf *m0 = NULL; uint8_t rate; DPRINTF(sc, 0, "\n"); USBD_CHECK_STATUS(xfer); tr_error: DPRINTF(sc, 10, "transfer error: %s\n", usbd_errstr(xfer->error)); if (xfer->error != USBD_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= ZYD_FLAG_BULK_WRITE_STALL; usbd_transfer_start(sc->sc_xfer[ZYD_TR_BULK_CS_WR]); } ifp->if_oerrors++; return; tr_transferred: DPRINTF(sc, 10, "transfer complete\n"); ifp->if_opackets++; tr_setup: if (sc->sc_flags & ZYD_FLAG_BULK_WRITE_STALL) { usbd_transfer_start(sc->sc_xfer[ZYD_TR_BULK_CS_WR]); return; } if (sc->sc_flags & ZYD_FLAG_WAIT_COMMAND) { /* don't send anything while a command * is pending ! */ return; } IF_DEQUEUE(&ic->ic_mgtq, m0); if (m0) { ni = (void *)m0->m_pkthdr.rcvif; m0->m_pkthdr.rcvif = NULL; if (ic->ic_rawbpf != NULL) { bpf_mtap(ic->ic_rawbpf, m0); } rate = IEEE80211_IS_CHAN_5GHZ(ic->ic_bss->ni_chan) ? 12 : 2; wh = mtod(m0, struct ieee80211_frame *); if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { uint16_t dur; dur = zyd_txtime(ZYD_ACK_SIZE, rate, ic->ic_flags) + ZYD_SIFS; *(uint16_t *)wh->i_dur = htole16(dur); #if 0 // tell hardware to add timestamp for probe responses if ((wh->i_fc[0] & (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) == (IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_RESP)) flags |= RAL_TX_TIMESTAMP; #endif } if (zyd_tx_frame(xfer, m0, ni, rate)) { goto error; } return; } if (ic->ic_state != IEEE80211_S_RUN) { return; } if (sc->sc_flags & ZYD_FLAG_TX_BEACON) { sc->sc_flags &= ~ZYD_FLAG_TX_BEACON; /* * Transmit beacon frame */ m0 = ieee80211_beacon_alloc(ic, ic->ic_bss, &(sc->sc_bo)); if (m0 == NULL) { DPRINTF(sc, -1, "could not allocate beacon frame!\n"); goto error; } rate = IEEE80211_IS_CHAN_5GHZ(ic->ic_bss->ni_chan) ? 12 : 4; if (zyd_tx_frame(xfer, m0, NULL, rate)) { goto error; } return; } IFQ_DEQUEUE(&ifp->if_snd, m0); if (m0) { if (m0->m_len < sizeof(struct ether_header)) { m0 = m_pullup(m0, sizeof(struct ether_header)); if (m0 == NULL) { goto error; } } eh = mtod(m0, struct ether_header *); ni = ieee80211_find_txnode(ic, eh->ether_dhost); if (ni == NULL) { goto error; } BPF_MTAP(ifp, m0); m0 = ieee80211_encap(ic, m0, ni); if (m0 == NULL) { goto error; } if (ic->ic_rawbpf != NULL) { bpf_mtap(ic->ic_rawbpf, m0); } /* XXX this should be reworked! */ if (ic->ic_fixed_rate != -1) { if (ic->ic_curmode != IEEE80211_MODE_AUTO) rs = &ic->ic_sup_rates[ic->ic_curmode]; else rs = &ic->ic_sup_rates[IEEE80211_MODE_11G]; rate = rs->rs_rates[ic->ic_fixed_rate]; } else { rs = &ni->ni_rates; rate = rs->rs_rates[ni->ni_txrate]; } rate &= IEEE80211_RATE_VAL; #if 0 if (ic->ic_flags & IEEE80211_F_WEPON) { m0 = ieee80211_wep_crypt(ifp, m0, 1); if (m0 == NULL) return ENOBUFS; } #endif #if 0 wh = mtod(m0, struct ieee80211_frame *); if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { dur = zyd_txtime(ZYD_ACK_SIZE, zyd_ack_rate(ic, rate), ic->ic_flags) + ZYD_SIFS; *(uint16_t *)wh->i_dur = htole16(dur); } #endif if (zyd_tx_frame(xfer, m0, ni, rate)) { goto error; } return; } return; error: if (m0) { m_freem(m0); m0 = NULL; } if (ni) { ieee80211_free_node(ni); ni = NULL; } ifp->if_oerrors++; goto tr_setup; } static void zyd_init_cb(void *arg) { struct zyd_softc *sc = arg; mtx_lock(&(sc->sc_mtx)); usbd_config_td_queue_command (&(sc->sc_config_td), &zyd_cfg_init_f1, 0); mtx_unlock(&(sc->sc_mtx)); return; } /* * This function allows for fast channel switching in monitor mode (used by * net-mgmt/kismet). In IBSS mode, we must explicitly reset the interface to * generate a new beacon frame. */ static int zyd_reset_cb(struct ifnet *ifp) { struct zyd_softc *sc = ifp->if_softc; struct ieee80211com *ic = &(sc->sc_ic); int error = 0; mtx_lock(&(sc->sc_mtx)); if (ic->ic_opmode != IEEE80211_M_MONITOR) { error = ENETRESET; goto done; } usbd_config_td_queue_command (&(sc->sc_config_td), &zyd_cfg_set_chan, 0); done: mtx_unlock(&(sc->sc_mtx)); return 0; } static int zyd_ioctl_cb(struct ifnet *ifp, u_long command, caddr_t data) { struct zyd_softc *sc = ifp->if_softc; struct ieee80211com *ic = &(sc->sc_ic); int error = 0; mtx_lock(&(sc->sc_mtx)); switch (command) { case SIOCSIFFLAGS: if (ifp->if_flags & IFF_UP) { if (ifp->if_drv_flags & IFF_DRV_RUNNING) { usbd_config_td_queue_command (&(sc->sc_config_td), &zyd_cfg_update_promisc, 0); } else { usbd_config_td_queue_command (&(sc->sc_config_td), &zyd_cfg_init_f1, 0); } } else { if (ifp->if_drv_flags & IFF_DRV_RUNNING) { usbd_config_td_queue_command (&(sc->sc_config_td), &zyd_cfg_stop_f1, 0); } } break; default: error = ieee80211_ioctl(ic, command, data); break; } if (error == ENETRESET) { if ((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING) && (ic->ic_roaming != IEEE80211_ROAMING_MANUAL)) { usbd_config_td_queue_command (&(sc->sc_config_td), &zyd_cfg_init_f1, 0); } error = 0; } mtx_unlock(&(sc->sc_mtx)); return error; } static void zyd_watchdog(void *arg) { struct zyd_softc *sc = arg; struct ieee80211com *ic = &(sc->sc_ic); mtx_assert(&(sc->sc_mtx), MA_OWNED); DPRINTF(sc, 0, "\n"); ieee80211_watchdog(ic); __callout_reset(&(sc->sc_watchdog), hz, &zyd_watchdog, sc); mtx_unlock(&(sc->sc_mtx)); return; } /* * This function is called periodically (every 200ms) during scanning to * switch from one channel to another. */ static void zyd_next_scan(void *arg) { struct zyd_softc *sc = arg; struct ieee80211com *ic = &sc->sc_ic; mtx_assert(&(sc->sc_mtx), MA_OWNED); DPRINTF(sc, 0, "executing next_scan\n"); if (ic->ic_state == IEEE80211_S_SCAN) ieee80211_next_scan(ic); mtx_unlock(&(sc->sc_mtx)); return; } static void zyd_config_copy(struct zyd_softc *sc, struct zyd_config_copy *cc, u_int16_t refcount) { struct ieee80211com *ic = &(sc->sc_ic); struct ieee80211_channel *c = ic->ic_curchan; struct ifnet *ifp = ic->ic_ifp; u_int8_t n; bzero(cc, sizeof(*cc)); if (c) { cc->ic_curchan.chan_to_ieee = ieee80211_chan2ieee(ic, c); if (c != IEEE80211_CHAN_ANYC) { cc->ic_curchan.chan_is_2ghz = IEEE80211_IS_CHAN_2GHZ(c) ? 1 : 0; } } if (ic->ic_bss) { if ((ic->ic_bss->ni_chan) && (ic->ic_bss->ni_chan != IEEE80211_CHAN_ANYC)) { cc->ic_bss.ni_chan.chan_is_5ghz = IEEE80211_IS_CHAN_5GHZ(ic->ic_bss->ni_chan) ? 1 : 0; } cc->ic_bss.ni_intval = ic->ic_bss->ni_intval; bcopy(ic->ic_bss->ni_bssid, cc->ic_bss.ni_bssid, sizeof(cc->ic_bss.ni_bssid)); } for (n = 0; n < IEEE80211_WEP_NKID; n++) { cc->ic_crypto.cs_nw_keys[n].wk_keyix = ic->ic_crypto.cs_nw_keys[n].wk_keyix; bcopy(ic->ic_crypto.cs_nw_keys[n].wk_key, cc->ic_crypto.cs_nw_keys[n].wk_key, IEEE80211_KEYBUF_SIZE); } cc->ic_opmode = ic->ic_opmode; cc->ic_state = ic->ic_state; cc->ic_flags = ic->ic_flags; if (ifp) { cc->if_flags = ifp->if_flags; } cc->ic_txpowlimit = ic->ic_txpowlimit; cc->ic_curmode = ic->ic_curmode; bcopy(ic->ic_myaddr, cc->ic_myaddr, sizeof(cc->ic_myaddr)); sc->sc_flags |= ZYD_FLAG_WAIT_COMMAND; return; } static void zyd_end_of_commands(struct zyd_softc *sc) { sc->sc_flags &= ~ZYD_FLAG_WAIT_COMMAND; zyd_start_transfers(sc); return; } pwcbsd/usb/if_uralreg.h000644 000423 000000 00000014040 10551670753 015667 0ustar00luigiwheel000000 000000 /* $FreeBSD: src/sys/dev/usb/if_uralreg.h,v 1.5 2006/01/21 10:25:51 damien Exp $ */ /*- * Copyright (c) 2005, 2006 * Damien Bergamini * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #define RAL_RX_DESC_SIZE (sizeof (struct ural_rx_desc)) #define RAL_TX_DESC_SIZE (sizeof (struct ural_tx_desc)) #define RAL_CONFIG_NO 1 #define RAL_IFACE_INDEX 0 #define RAL_VENDOR_REQUEST 0x01 #define RAL_WRITE_MAC 0x02 #define RAL_READ_MAC 0x03 #define RAL_WRITE_MULTI_MAC 0x06 #define RAL_READ_MULTI_MAC 0x07 #define RAL_READ_EEPROM 0x09 /* MAC registers. */ #define RAL_MAC_CSR0 0x0400 /* ASIC Version */ #define RAL_MAC_CSR1 0x0402 /* System control */ #define RAL_MAC_CSR2 0x0404 /* MAC addr0 */ #define RAL_MAC_CSR3 0x0406 /* MAC addr1 */ #define RAL_MAC_CSR4 0x0408 /* MAC addr2 */ #define RAL_MAC_CSR5 0x040a /* BSSID0 */ #define RAL_MAC_CSR6 0x040c /* BSSID1 */ #define RAL_MAC_CSR7 0x040e /* BSSID2 */ #define RAL_MAC_CSR8 0x0410 /* Max frame length */ #define RAL_MAC_CSR9 0x0412 /* Timer control */ #define RAL_MAC_CSR10 0x0414 /* Slot time */ #define RAL_MAC_CSR11 0x0416 /* IFS */ #define RAL_MAC_CSR12 0x0418 /* EIFS */ #define RAL_MAC_CSR13 0x041a /* Power mode0 */ #define RAL_MAC_CSR14 0x041c /* Power mode1 */ #define RAL_MAC_CSR15 0x041e /* Power saving transition0 */ #define RAL_MAC_CSR16 0x0420 /* Power saving transition1 */ #define RAL_MAC_CSR17 0x0422 /* Power state control */ #define RAL_MAC_CSR18 0x0424 /* Auto wake-up control */ #define RAL_MAC_CSR19 0x0426 /* GPIO control */ #define RAL_MAC_CSR20 0x0428 /* LED control0 */ #define RAL_MAC_CSR22 0x042c /* XXX not documented */ /* Tx/Rx Registers. */ #define RAL_TXRX_CSR0 0x0440 /* Security control */ #define RAL_TXRX_CSR2 0x0444 /* Rx control */ #define RAL_TXRX_CSR5 0x044a /* CCK Tx BBP ID0 */ #define RAL_TXRX_CSR6 0x044c /* CCK Tx BBP ID1 */ #define RAL_TXRX_CSR7 0x044e /* OFDM Tx BBP ID0 */ #define RAL_TXRX_CSR8 0x0450 /* OFDM Tx BBP ID1 */ #define RAL_TXRX_CSR10 0x0454 /* Auto responder control */ #define RAL_TXRX_CSR11 0x0456 /* Auto responder basic rate */ #define RAL_TXRX_CSR18 0x0464 /* Beacon interval */ #define RAL_TXRX_CSR19 0x0466 /* Beacon/sync control */ #define RAL_TXRX_CSR20 0x0468 /* Beacon alignment */ #define RAL_TXRX_CSR21 0x046a /* XXX not documented */ /* Security registers. */ #define RAL_SEC_CSR0 0x0480 /* Shared key 0, word 0 */ /* PHY registers. */ #define RAL_PHY_CSR2 0x04c4 /* Tx MAC configuration */ #define RAL_PHY_CSR4 0x04c8 /* Interface configuration */ #define RAL_PHY_CSR5 0x04ca /* BBP Pre-Tx CCK */ #define RAL_PHY_CSR6 0x04cc /* BBP Pre-Tx OFDM */ #define RAL_PHY_CSR7 0x04ce /* BBP serial control */ #define RAL_PHY_CSR8 0x04d0 /* BBP serial status */ #define RAL_PHY_CSR9 0x04d2 /* RF serial control0 */ #define RAL_PHY_CSR10 0x04d4 /* RF serial control1 */ /* Statistics registers. */ #define RAL_STA_CSR0 0x04e0 /* FCS error */ #define RAL_DISABLE_RX (1 << 0) #define RAL_DROP_CRC (1 << 1) #define RAL_DROP_PHY (1 << 2) #define RAL_DROP_CTL (1 << 3) #define RAL_DROP_NOT_TO_ME (1 << 4) #define RAL_DROP_TODS (1 << 5) #define RAL_DROP_BAD_VERSION (1 << 6) #define RAL_DROP_MULTICAST (1 << 9) #define RAL_DROP_BROADCAST (1 << 10) #define RAL_SHORT_PREAMBLE (1 << 2) #define RAL_RESET_ASIC (1 << 0) #define RAL_RESET_BBP (1 << 1) #define RAL_HOST_READY (1 << 2) #define RAL_ENABLE_TSF (1 << 0) #define RAL_ENABLE_TSF_SYNC(x) (((x) & 0x3) << 1) #define RAL_ENABLE_TBCN (1 << 3) #define RAL_ENABLE_BEACON_GENERATOR (1 << 4) #define RAL_RF_AWAKE (3 << 7) #define RAL_BBP_AWAKE (3 << 5) #define RAL_BBP_WRITE (1 << 15) #define RAL_BBP_BUSY (1 << 0) #define RAL_RF1_AUTOTUNE 0x08000 #define RAL_RF3_AUTOTUNE 0x00040 #define RAL_RF_2522 0x00 #define RAL_RF_2523 0x01 #define RAL_RF_2524 0x02 #define RAL_RF_2525 0x03 #define RAL_RF_2525E 0x04 #define RAL_RF_2526 0x05 /* dual-band RF */ #define RAL_RF_5222 0x10 #define RAL_BBP_VERSION 0 #define RAL_BBP_TX 2 #define RAL_BBP_RX 14 #define RAL_BBP_ANTA 0x00 #define RAL_BBP_DIVERSITY 0x01 #define RAL_BBP_ANTB 0x02 #define RAL_BBP_ANTMASK 0x03 #define RAL_BBP_FLIPIQ 0x04 #define RAL_JAPAN_FILTER 0x08 #define RAL_RF_LOBUSY (1 << 15) #define RAL_RF_BUSY (1 << 31) #define RAL_RF_20BIT (20 << 24) #define RAL_RF1 0 #define RAL_RF2 2 #define RAL_RF3 1 #define RAL_RF4 3 #define RAL_EEPROM_ADDRESS 0x0004 #define RAL_EEPROM_TXPOWER 0x003c #define RAL_EEPROM_CONFIG0 0x0016 #define RAL_EEPROM_BBP_BASE 0x001c struct ural_tx_desc { uint32_t flags; #define RAL_TX_RETRY(x) ((x) << 4) #define RAL_TX_MORE_FRAG (1 << 8) #define RAL_TX_ACK (1 << 9) #define RAL_TX_TIMESTAMP (1 << 10) #define RAL_TX_OFDM (1 << 11) #define RAL_TX_NEWSEQ (1 << 12) #define RAL_TX_IFS_MASK 0x00006000 #define RAL_TX_IFS_BACKOFF (0 << 13) #define RAL_TX_IFS_SIFS (1 << 13) #define RAL_TX_IFS_NEWBACKOFF (2 << 13) #define RAL_TX_IFS_NONE (3 << 13) uint16_t wme; #define RAL_LOGCWMAX(x) (((x) & 0xf) << 12) #define RAL_LOGCWMIN(x) (((x) & 0xf) << 8) #define RAL_AIFSN(x) (((x) & 0x3) << 6) #define RAL_IVOFFSET(x) (((x) & 0x3f)) uint16_t reserved1; uint8_t plcp_signal; uint8_t plcp_service; #define RAL_PLCP_LENGEXT 0x80 uint8_t plcp_length_lo; uint8_t plcp_length_hi; uint32_t iv; uint32_t eiv; } __packed; struct ural_rx_desc { uint32_t flags; #define RAL_RX_CRC_ERROR (1 << 5) #define RAL_RX_OFDM (1 << 6) #define RAL_RX_PHY_ERROR (1 << 7) uint8_t rssi; uint8_t rate; uint16_t reserved; uint32_t iv; uint32_t eiv; } __packed; pwcbsd/usb/if_uralvar.h000644 000423 000000 00000010073 10551670753 015704 0ustar00luigiwheel000000 000000 /* $FreeBSD: src/sys/dev/usb/if_uralvar.h,v 1.7 2006/09/07 00:06:41 imp Exp $ */ /*- * Copyright (c) 2005 * Damien Bergamini * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #define URAL_N_TRANSFER 4 struct ural_config_copy { struct { uint32_t chan_to_ieee; uint8_t chan_is_2ghz; } ic_curchan; struct { struct { uint8_t chan_is_5ghz; } ni_chan; uint16_t ni_intval; uint8_t ni_bssid[IEEE80211_ADDR_LEN]; } ic_bss; struct { struct { ieee80211_keyix wk_keyix; uint8_t wk_key[IEEE80211_KEYBUF_SIZE]; } cs_nw_keys[IEEE80211_WEP_NKID]; } ic_crypto; enum ieee80211_opmode ic_opmode; enum ieee80211_state ic_state; uint32_t ic_flags; uint32_t if_flags; uint16_t ic_txpowlimit; uint16_t ic_curmode; uint8_t ic_myaddr[IEEE80211_ADDR_LEN]; }; struct ural_rx_radiotap_header { struct ieee80211_radiotap_header wr_ihdr; uint8_t wr_flags; uint8_t wr_rate; uint16_t wr_chan_freq; uint16_t wr_chan_flags; uint8_t wr_antenna; uint8_t wr_antsignal; }; #define RAL_RX_RADIOTAP_PRESENT \ ((1 << IEEE80211_RADIOTAP_FLAGS) | \ (1 << IEEE80211_RADIOTAP_RATE) | \ (1 << IEEE80211_RADIOTAP_CHANNEL) | \ (1 << IEEE80211_RADIOTAP_ANTENNA) | \ (1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL)) struct ural_tx_radiotap_header { struct ieee80211_radiotap_header wt_ihdr; uint8_t wt_flags; uint8_t wt_rate; uint16_t wt_chan_freq; uint16_t wt_chan_flags; uint8_t wt_antenna; }; #define RAL_TX_RADIOTAP_PRESENT \ ((1 << IEEE80211_RADIOTAP_FLAGS) | \ (1 << IEEE80211_RADIOTAP_RATE) | \ (1 << IEEE80211_RADIOTAP_CHANNEL) | \ (1 << IEEE80211_RADIOTAP_ANTENNA)) struct ural_amrr { int txcnt; int retrycnt; int success; int success_threshold; int recovery; }; union ural_rxtap { struct ural_rx_radiotap_header h; uint8_t pad[64]; }; union ural_txtap { struct ural_tx_radiotap_header h; uint8_t pad[64]; }; struct ural_bbp_prom { uint8_t val; uint8_t reg; } __packed; struct ural_softc { struct usbd_config_td sc_config_td; struct ural_tx_desc sc_tx_desc; struct ural_rx_desc sc_rx_desc; struct ieee80211com sc_ic; struct ural_amrr sc_amrr; struct ieee80211_beacon_offsets sc_bo; struct mtx sc_mtx; struct __callout sc_watchdog; struct ural_bbp_prom sc_bbp_prom[16]; struct usbd_xfer *sc_xfer[URAL_N_TRANSFER]; union ural_rxtap sc_rxtap; union ural_txtap sc_txtap; struct mbuf *sc_bcn_mbuf; struct ifnet *sc_ifp; struct bpf_if *sc_drvbpf; struct usbd_device *sc_udev; int (*sc_newstate) (struct ieee80211com *, enum ieee80211_state, int); enum ieee80211_state sc_state; uint32_t sc_bcn_flags; uint32_t sc_unit; uint32_t sc_asic_rev; uint32_t sc_rf_regs[4]; uint16_t sc_flags; #define URAL_FLAG_READ_STALL 0x0001 #define URAL_FLAG_WRITE_STALL 0x0002 #define URAL_FLAG_SEND_BYTE_FRAME 0x0004 #define URAL_FLAG_SEND_BCN_FRAME 0x0008 #define URAL_FLAG_LL_READY 0x0010 #define URAL_FLAG_HL_READY 0x0020 #define URAL_FLAG_WAIT_COMMAND 0x0040 uint16_t sc_txtap_len; uint16_t sc_rxtap_len; uint16_t sc_sta[11]; uint16_t sc_bcn_rate; uint8_t sc_rf_rev; uint8_t sc_txpow[14]; uint8_t sc_led_mode; uint8_t sc_hw_radio; uint8_t sc_rx_ant; uint8_t sc_tx_ant; uint8_t sc_nb_ant; uint8_t sc_if_timer; uint8_t sc_tx_timer; uint8_t sc_scan_timer; uint8_t sc_amrr_timer; uint8_t sc_name[32]; }; pwcbsd/usb/if_zydfw.h000644 000423 000000 00000124275 10551670753 015405 0ustar00luigiwheel000000 000000 /* * Copyright (c) 2006 by Hans Petter Selasky * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * This file contains firmware for the Zydas 1211 chip. * The current firware version is v2.13.0.0: */ static const uint8_t zyd_firmware[] = { 0x08, 0x91, 0xff, 0xed, 0x09, 0x93, 0x1e, 0xee, /* 0x0000 */ 0xd1, 0x94, 0x11, 0xee, 0x88, 0xd4, 0xd1, 0x96, /* 0x0008 */ 0xd1, 0x98, 0x5c, 0x99, 0x5c, 0x99, 0x4c, 0x99, /* 0x0010 */ 0x04, 0x9d, 0xd1, 0x98, 0xd1, 0x9a, 0x03, 0xee, /* 0x0018 */ 0xf4, 0x94, 0xd3, 0xd4, 0x41, 0x2a, 0x40, 0x4a, /* 0x0020 */ 0x45, 0xbe, 0x88, 0x92, 0x41, 0x24, 0x40, 0x44, /* 0x0028 */ 0x53, 0xbe, 0x40, 0xf0, 0x93, 0xee, 0x41, 0xee, /* 0x0030 */ 0x98, 0x9a, 0xd4, 0xf7, 0x02, 0x00, 0x1f, 0xec, /* 0x0038 */ 0x00, 0x00, 0xb2, 0xf8, 0x4d, 0x00, 0xa1, 0xec, /* 0x0040 */ 0x00, 0x00, 0xa6, 0xf7, 0x21, 0x00, 0x00, 0x00, /* 0x0048 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0xd8, /* 0x0050 */ 0xa0, 0x90, 0x98, 0x9a, 0x98, 0x9a, 0xa0, 0xd8, /* 0x0058 */ 0x40, 0xf0, 0xb4, 0xf0, 0xa0, 0x90, 0x98, 0x9a, /* 0x0060 */ 0xa0, 0xd8, 0x40, 0xf0, 0x64, 0xef, 0xa0, 0x90, /* 0x0068 */ 0x98, 0x9a, 0xa0, 0xd8, 0x40, 0xf0, 0xf6, 0xf0, /* 0x0070 */ 0xa0, 0x90, 0x98, 0x9a, 0xa0, 0xd8, 0x40, 0xf0, /* 0x0078 */ 0xf7, 0xf6, 0xa0, 0x90, 0x98, 0x9a, 0xa0, 0xd8, /* 0x0080 */ 0x40, 0xf0, 0xf8, 0xf5, 0xa0, 0x90, 0x98, 0x9a, /* 0x0088 */ 0xa0, 0xd8, 0x40, 0xf0, 0xf1, 0xf0, 0xa0, 0x90, /* 0x0090 */ 0x98, 0x9a, 0x98, 0x9a, 0xa0, 0xd8, 0x40, 0xf0, /* 0x0098 */ 0x97, 0xf7, 0xa0, 0x90, 0x98, 0x9a, 0x88, 0xda, /* 0x00a0 */ 0x08, 0x0b, 0x01, 0x00, 0x0d, 0x03, 0x03, 0x00, /* 0x00a8 */ 0x09, 0x05, 0x01, 0x00, 0xc2, 0x94, 0x42, 0x02, /* 0x00b0 */ 0xc1, 0x92, 0x03, 0x96, 0x1b, 0xd7, 0x2a, 0x86, /* 0x00b8 */ 0x1a, 0xd5, 0x2b, 0x86, 0x09, 0xa3, 0x00, 0x80, /* 0x00c0 */ 0x19, 0xd3, 0x2c, 0x86, 0x00, 0xee, 0x0a, 0x65, /* 0x00c8 */ 0xc0, 0x7a, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3, /* 0x00d0 */ 0xfe, 0xff, 0xc2, 0xd2, 0x88, 0x98, 0x90, 0x9a, /* 0x00d8 */ 0x88, 0xda, 0x42, 0x20, 0x08, 0x0b, 0x01, 0x00, /* 0x00e0 */ 0x0d, 0x03, 0x05, 0x00, 0x05, 0x94, 0xc5, 0xd4, /* 0x00e8 */ 0x09, 0x05, 0x01, 0x00, 0xc2, 0x94, 0x01, 0xd4, /* 0x00f0 */ 0x42, 0x02, 0xc1, 0x96, 0x0a, 0x65, 0xc0, 0x7a, /* 0x00f8 */ 0x02, 0x99, 0xc4, 0x92, 0x41, 0xa2, 0xc4, 0xd2, /* 0x0100 */ 0xc5, 0x98, 0x1c, 0xd9, 0x2a, 0x86, 0x01, 0x98, /* 0x0108 */ 0x1c, 0xd9, 0x2b, 0x86, 0x1b, 0xd7, 0x2c, 0x86, /* 0x0110 */ 0x00, 0xee, 0x09, 0xb3, 0xfe, 0xff, 0xc2, 0xd2, /* 0x0118 */ 0x42, 0x00, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, /* 0x0120 */ 0x41, 0x20, 0x08, 0x0b, 0x01, 0x00, 0x40, 0xf0, /* 0x0128 */ 0xe5, 0xee, 0x11, 0x93, 0xd8, 0xf7, 0x41, 0x42, /* 0x0130 */ 0x02, 0x5e, 0x0f, 0x9f, 0xae, 0xee, 0x40, 0xf1, /* 0x0138 */ 0x40, 0x92, 0x19, 0xd3, 0xd8, 0xf7, 0xc5, 0x92, /* 0x0140 */ 0x41, 0x92, 0x19, 0xd3, 0x00, 0x83, 0x40, 0x92, /* 0x0148 */ 0x19, 0xd3, 0x00, 0x83, 0x0f, 0x9f, 0x95, 0xf8, /* 0x0150 */ 0x0f, 0x9f, 0x99, 0xee, 0x42, 0x42, 0x02, 0x5e, /* 0x0158 */ 0x0f, 0x9f, 0x99, 0xee, 0x40, 0x92, 0x19, 0xd3, /* 0x0160 */ 0xd8, 0xf7, 0x09, 0x93, 0xc7, 0xf7, 0x19, 0xd3, /* 0x0168 */ 0x91, 0xec, 0x40, 0xf0, 0x5f, 0xf2, 0x09, 0x63, /* 0x0170 */ 0x00, 0x80, 0x19, 0xd3, 0xf2, 0xbd, 0x0f, 0x9f, /* 0x0178 */ 0x99, 0xee, 0x41, 0x00, 0x88, 0x98, 0x90, 0x9a, /* 0x0180 */ 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x40, 0x92, /* 0x0188 */ 0x19, 0xd3, 0x12, 0x95, 0x19, 0xd3, 0x10, 0x95, /* 0x0190 */ 0x19, 0xd3, 0x02, 0x80, 0x19, 0xd3, 0x03, 0x82, /* 0x0198 */ 0x09, 0x93, 0xc7, 0xf7, 0x19, 0xd3, 0x91, 0xec, /* 0x01a0 */ 0x40, 0xf0, 0x5f, 0xf2, 0x40, 0xf0, 0xde, 0xf3, /* 0x01a8 */ 0x11, 0x93, 0x04, 0xec, 0x42, 0x42, 0x02, 0x5e, /* 0x01b0 */ 0x0f, 0x9f, 0xe3, 0xee, 0x40, 0x92, 0x19, 0xd3, /* 0x01b8 */ 0x04, 0xec, 0x40, 0xf0, 0x38, 0xf2, 0x88, 0x98, /* 0x01c0 */ 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, /* 0x01c8 */ 0x11, 0x93, 0x44, 0x96, 0x09, 0xb3, 0xff, 0xfd, /* 0x01d0 */ 0x19, 0xd3, 0x44, 0x96, 0x40, 0xf0, 0x90, 0xf7, /* 0x01d8 */ 0x6e, 0x92, 0x19, 0xd3, 0x05, 0x84, 0x40, 0xf0, /* 0x01e0 */ 0xc4, 0xee, 0x4b, 0x62, 0x0a, 0x95, 0x2e, 0xee, /* 0x01e8 */ 0xd1, 0xd4, 0x0b, 0x97, 0x2b, 0xee, 0xd1, 0xd6, /* 0x01f0 */ 0x0a, 0x95, 0x00, 0xee, 0xd1, 0xd4, 0x0b, 0x97, /* 0x01f8 */ 0x2f, 0xee, 0xd1, 0xd6, 0x0a, 0x95, 0x34, 0xee, /* 0x0200 */ 0xd1, 0xd4, 0x0b, 0x97, 0x39, 0xee, 0xd1, 0xd6, /* 0x0208 */ 0x0a, 0x95, 0x3e, 0xee, 0xd1, 0xd4, 0x0b, 0x97, /* 0x0210 */ 0x43, 0xee, 0xd1, 0xd6, 0x0a, 0x95, 0x48, 0xee, /* 0x0218 */ 0xd1, 0xd4, 0x0b, 0x97, 0x4d, 0xee, 0xd1, 0xd6, /* 0x0220 */ 0x0a, 0x95, 0x4e, 0xee, 0xc1, 0xd4, 0x0a, 0x65, /* 0x0228 */ 0x00, 0x44, 0x02, 0x97, 0xc3, 0x92, 0x44, 0xa2, /* 0x0230 */ 0xc2, 0xd2, 0x43, 0xf1, 0x09, 0x93, 0x01, 0x3f, /* 0x0238 */ 0x19, 0xd3, 0xc0, 0x85, 0x11, 0x93, 0x44, 0x96, /* 0x0240 */ 0x09, 0xb3, 0xff, 0xfc, 0x19, 0xd3, 0x44, 0x96, /* 0x0248 */ 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, /* 0x0250 */ 0x01, 0x00, 0x0d, 0x03, 0x03, 0x00, 0x03, 0x96, /* 0x0258 */ 0x41, 0x02, 0x03, 0x99, 0xc4, 0x94, 0x42, 0x04, /* 0x0260 */ 0xc1, 0x04, 0xc2, 0x94, 0xc3, 0xd4, 0x88, 0x98, /* 0x0268 */ 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, /* 0x0270 */ 0x40, 0x92, 0x19, 0xd3, 0x94, 0xec, 0x13, 0x97, /* 0x0278 */ 0x95, 0xec, 0x1b, 0xd7, 0x02, 0x80, 0x11, 0x93, /* 0x0280 */ 0x99, 0xec, 0x19, 0xd3, 0x7c, 0x96, 0x0b, 0x97, /* 0x0288 */ 0xa0, 0x00, 0x1b, 0xd7, 0x6e, 0xec, 0x0a, 0x65, /* 0x0290 */ 0x0e, 0x42, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3, /* 0x0298 */ 0xff, 0xbf, 0x11, 0xa3, 0x9a, 0xec, 0xc2, 0xd2, /* 0x02a0 */ 0x0a, 0x65, 0xeb, 0x43, 0x02, 0x97, 0xc3, 0x92, /* 0x02a8 */ 0x09, 0xa3, 0xc0, 0x00, 0xc2, 0xd2, 0x0a, 0x65, /* 0x02b0 */ 0xe9, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3, /* 0x02b8 */ 0xbf, 0xff, 0xc2, 0xd2, 0x88, 0x98, 0x90, 0x9a, /* 0x02c0 */ 0x88, 0xda, 0x47, 0x20, 0x08, 0x0b, 0x01, 0x00, /* 0x02c8 */ 0x14, 0x99, 0x03, 0x80, 0x0c, 0xb3, 0x00, 0x10, /* 0x02d0 */ 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x97, 0xf0, /* 0x02d8 */ 0x11, 0x93, 0x9f, 0xec, 0x41, 0x02, 0x19, 0xd3, /* 0x02e0 */ 0x9f, 0xec, 0x11, 0x93, 0xd6, 0xf7, 0x40, 0x42, /* 0x02e8 */ 0x02, 0x4e, 0x0f, 0x9f, 0x84, 0xef, 0x0a, 0x65, /* 0x02f0 */ 0xfe, 0x7f, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xa3, /* 0x02f8 */ 0x00, 0x04, 0xc2, 0xd2, 0x0f, 0x9f, 0xb1, 0xf0, /* 0x0300 */ 0x11, 0x93, 0x94, 0xec, 0x02, 0xd2, 0x40, 0x42, /* 0x0308 */ 0x02, 0x5e, 0x0f, 0x9f, 0xd0, 0xef, 0x41, 0x92, /* 0x0310 */ 0x19, 0xd3, 0x94, 0xec, 0x19, 0xd3, 0x9f, 0xec, /* 0x0318 */ 0x12, 0x95, 0x02, 0x80, 0x1a, 0xd5, 0x95, 0xec, /* 0x0320 */ 0x13, 0x97, 0x7c, 0x96, 0x1b, 0xd7, 0x99, 0xec, /* 0x0328 */ 0x0a, 0x65, 0x0e, 0x42, 0x02, 0x97, 0xc3, 0x92, /* 0x0330 */ 0x09, 0xb3, 0x00, 0x40, 0x19, 0xd3, 0x9a, 0xec, /* 0x0338 */ 0x09, 0x63, 0x00, 0x40, 0xc2, 0xd2, 0x02, 0x94, /* 0x0340 */ 0x1a, 0xd5, 0x7c, 0x96, 0x0c, 0xb3, 0x00, 0x08, /* 0x0348 */ 0x40, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0xb0, 0xef, /* 0x0350 */ 0x0c, 0xb3, 0xff, 0x07, 0x0f, 0x9f, 0xb4, 0xef, /* 0x0358 */ 0x11, 0x93, 0x06, 0x80, 0x09, 0xb3, 0xff, 0x07, /* 0x0360 */ 0x09, 0x03, 0x00, 0xa0, 0x19, 0xd3, 0x97, 0xec, /* 0x0368 */ 0x40, 0x98, 0x0b, 0x97, 0x9c, 0xec, 0x04, 0x95, /* 0x0370 */ 0x03, 0x05, 0x14, 0x03, 0x97, 0xec, 0x46, 0x02, /* 0x0378 */ 0xc1, 0x92, 0xc2, 0xd2, 0x41, 0x08, 0x42, 0x48, /* 0x0380 */ 0x02, 0x9e, 0x0f, 0x9f, 0xbb, 0xef, 0x11, 0x93, /* 0x0388 */ 0x97, 0xec, 0xc1, 0x92, 0xc5, 0xd2, 0x5f, 0xb2, /* 0x0390 */ 0x19, 0xd3, 0x9b, 0xec, 0x0f, 0x9f, 0xd3, 0xef, /* 0x0398 */ 0x13, 0x97, 0x98, 0xec, 0xc5, 0xd6, 0x11, 0x93, /* 0x03a0 */ 0x03, 0x80, 0x09, 0xb3, 0x00, 0x08, 0x40, 0x42, /* 0x03a8 */ 0x02, 0x4e, 0x0f, 0x9f, 0xe9, 0xef, 0x11, 0x93, /* 0x03b0 */ 0xdc, 0xf7, 0x41, 0x02, 0x19, 0xd3, 0xdc, 0xf7, /* 0x03b8 */ 0x11, 0x93, 0xdb, 0xf7, 0x09, 0xa3, 0x00, 0x10, /* 0x03c0 */ 0x19, 0xd3, 0xdb, 0xf7, 0x40, 0x98, 0x1c, 0xd9, /* 0x03c8 */ 0x9b, 0xec, 0x12, 0x95, 0x9b, 0xec, 0x40, 0x44, /* 0x03d0 */ 0x02, 0x4e, 0x0f, 0x9f, 0x86, 0xf0, 0x0a, 0xb3, /* 0x03d8 */ 0x08, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, /* 0x03e0 */ 0x07, 0xf0, 0x0a, 0xb3, 0x07, 0x00, 0x09, 0x05, /* 0x03e8 */ 0xa9, 0xec, 0xc2, 0x94, 0x01, 0xd4, 0x09, 0x03, /* 0x03f0 */ 0xa1, 0xec, 0xc1, 0x92, 0x19, 0xd3, 0x9b, 0xec, /* 0x03f8 */ 0xc5, 0x94, 0x0a, 0xb5, 0x00, 0xff, 0x01, 0xa5, /* 0x0400 */ 0xc5, 0xd4, 0x0f, 0x9f, 0x13, 0xf0, 0x0a, 0x05, /* 0x0408 */ 0xff, 0xff, 0x0a, 0x03, 0xb1, 0xec, 0xc1, 0x92, /* 0x0410 */ 0x01, 0xd2, 0x1a, 0xd5, 0x9b, 0xec, 0xc5, 0x96, /* 0x0418 */ 0x0b, 0x07, 0xff, 0xff, 0xc5, 0xd6, 0x11, 0x93, /* 0x0420 */ 0x97, 0xec, 0xc5, 0x98, 0xc1, 0xd8, 0x11, 0x93, /* 0x0428 */ 0x97, 0xec, 0x09, 0x05, 0x0b, 0x00, 0x03, 0xd4, /* 0x0430 */ 0xc2, 0x96, 0x06, 0xd6, 0x7b, 0x95, 0x7a, 0x95, /* 0x0438 */ 0x4c, 0x02, 0xc1, 0x92, 0x59, 0x93, 0x59, 0x93, /* 0x0440 */ 0x01, 0xa5, 0x01, 0x98, 0x0c, 0xf5, 0x7b, 0x93, /* 0x0448 */ 0x09, 0x09, 0x01, 0x00, 0x06, 0x92, 0x09, 0xb3, /* 0x0450 */ 0xff, 0x00, 0x04, 0xd2, 0x5c, 0x93, 0x59, 0x93, /* 0x0458 */ 0x04, 0x94, 0x01, 0xa5, 0x03, 0x96, 0xc3, 0xd4, /* 0x0460 */ 0x11, 0x93, 0x97, 0xec, 0x4c, 0x02, 0x05, 0xd2, /* 0x0468 */ 0xc1, 0x92, 0x09, 0xb3, 0x00, 0xff, 0x7c, 0x95, /* 0x0470 */ 0x7a, 0x95, 0x02, 0xa3, 0x05, 0x98, 0xc4, 0xd2, /* 0x0478 */ 0x12, 0x95, 0x97, 0xec, 0x45, 0x04, 0x02, 0x97, /* 0x0480 */ 0xc3, 0x92, 0x09, 0xa3, 0x00, 0x01, 0xc2, 0xd2, /* 0x0488 */ 0x12, 0x95, 0x9b, 0xec, 0x0a, 0xb3, 0x08, 0x00, /* 0x0490 */ 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x5b, 0xf0, /* 0x0498 */ 0x12, 0x95, 0x97, 0xec, 0x4a, 0x04, 0x02, 0x99, /* 0x04a0 */ 0xc4, 0x92, 0x01, 0x98, 0x0c, 0xf3, 0x7b, 0x93, /* 0x04a8 */ 0x41, 0x02, 0x0f, 0x9f, 0x7c, 0xf0, 0x43, 0x44, /* 0x04b0 */ 0x02, 0x8e, 0x0f, 0x9f, 0x7d, 0xf0, 0x11, 0x93, /* 0x04b8 */ 0x97, 0xec, 0x42, 0x02, 0x0a, 0x05, 0xff, 0xff, /* 0x04c0 */ 0xc1, 0xd4, 0x11, 0x93, 0x97, 0xec, 0x4a, 0x02, /* 0x04c8 */ 0x12, 0x95, 0x60, 0x96, 0xc1, 0xd4, 0x12, 0x95, /* 0x04d0 */ 0x97, 0xec, 0x4b, 0x04, 0x02, 0x97, 0xc3, 0x92, /* 0x04d8 */ 0x09, 0xb3, 0x1f, 0xff, 0xc2, 0xd2, 0x12, 0x95, /* 0x04e0 */ 0x97, 0xec, 0x4b, 0x04, 0x11, 0x93, 0x62, 0x96, /* 0x04e8 */ 0x41, 0x93, 0x59, 0x93, 0x02, 0x99, 0xc4, 0xa2, /* 0x04f0 */ 0xc2, 0xd2, 0xc5, 0x92, 0x19, 0xd3, 0x98, 0xec, /* 0x04f8 */ 0x0a, 0x95, 0x0c, 0x02, 0x1a, 0xd5, 0x02, 0x80, /* 0x0500 */ 0x0f, 0x9f, 0xb1, 0xf0, 0x09, 0x63, 0xfe, 0x7f, /* 0x0508 */ 0x01, 0x97, 0xc3, 0x94, 0x0a, 0xa5, 0x00, 0x04, /* 0x0510 */ 0xc1, 0xd4, 0x11, 0x93, 0x9f, 0xec, 0x09, 0xa3, /* 0x0518 */ 0x00, 0x01, 0x19, 0xd3, 0x9f, 0xec, 0x40, 0xf0, /* 0x0520 */ 0x39, 0xef, 0x0f, 0x9f, 0xb1, 0xf0, 0x11, 0x93, /* 0x0528 */ 0x94, 0xec, 0x41, 0x42, 0x02, 0x5e, 0x0f, 0x9f, /* 0x0530 */ 0xa6, 0xf0, 0x40, 0xf0, 0x39, 0xef, 0x11, 0x93, /* 0x0538 */ 0x95, 0xec, 0x44, 0xb2, 0x40, 0x42, 0x02, 0x4e, /* 0x0540 */ 0x0f, 0x9f, 0xb1, 0xf0, 0x48, 0x98, 0x1c, 0xd9, /* 0x0548 */ 0x02, 0x80, 0x11, 0x93, 0x91, 0xec, 0x41, 0x22, /* 0x0550 */ 0x0a, 0x95, 0xb1, 0xf0, 0x88, 0xd4, 0x88, 0xdc, /* 0x0558 */ 0x91, 0x9a, 0x47, 0x00, 0x88, 0x98, 0x90, 0x9a, /* 0x0560 */ 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x11, 0x93, /* 0x0568 */ 0x04, 0x82, 0x48, 0xb2, 0x40, 0x42, 0x02, 0x4e, /* 0x0570 */ 0x0f, 0x9f, 0xc8, 0xf0, 0x0a, 0x65, 0xfd, 0x7d, /* 0x0578 */ 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3, 0xff, 0xfe, /* 0x0580 */ 0xc2, 0xd2, 0x41, 0x92, 0x19, 0xd3, 0xbf, 0xec, /* 0x0588 */ 0x11, 0x93, 0x04, 0x82, 0x43, 0xb2, 0x12, 0x95, /* 0x0590 */ 0x03, 0x82, 0x02, 0xb3, 0x40, 0x42, 0x02, 0x4e, /* 0x0598 */ 0x0f, 0x9f, 0xef, 0xf0, 0x0a, 0xb3, 0x00, 0xff, /* 0x05a0 */ 0x48, 0xa2, 0x19, 0xd3, 0x03, 0x82, 0x40, 0xf0, /* 0x05a8 */ 0xeb, 0xf3, 0x11, 0x93, 0xbf, 0xec, 0x41, 0x42, /* 0x05b0 */ 0x02, 0x5e, 0x0f, 0x9f, 0xef, 0xf0, 0x11, 0x93, /* 0x05b8 */ 0x07, 0x82, 0x11, 0x43, 0x03, 0xec, 0x02, 0x0e, /* 0x05c0 */ 0x0f, 0x9f, 0xef, 0xf0, 0x11, 0x93, 0x03, 0x82, /* 0x05c8 */ 0x09, 0xa3, 0x00, 0x01, 0x19, 0xd3, 0x03, 0x82, /* 0x05d0 */ 0x40, 0x96, 0x1b, 0xd7, 0xbf, 0xec, 0x88, 0x98, /* 0x05d8 */ 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, /* 0x05e0 */ 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, /* 0x05e8 */ 0x01, 0x00, 0x11, 0x93, 0x20, 0xbc, 0xc8, 0xd2, /* 0x05f0 */ 0x40, 0xf0, 0x48, 0xf1, 0x41, 0x00, 0x88, 0x98, /* 0x05f8 */ 0x90, 0x9a, 0x88, 0xda, 0x42, 0x20, 0x08, 0x0b, /* 0x0600 */ 0x01, 0x00, 0x0d, 0x03, 0x05, 0x00, 0x05, 0x94, /* 0x0608 */ 0x41, 0x02, 0xc1, 0x92, 0x01, 0x97, 0xc3, 0x96, /* 0x0610 */ 0xc2, 0xd6, 0x0a, 0x45, 0x00, 0x95, 0x02, 0x5e, /* 0x0618 */ 0x0f, 0x9f, 0x45, 0xf1, 0xc1, 0x92, 0x41, 0xb2, /* 0x0620 */ 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x45, 0xf1, /* 0x0628 */ 0x11, 0x93, 0xc0, 0xec, 0x40, 0x42, 0x02, 0x5e, /* 0x0630 */ 0x0f, 0x9f, 0x45, 0xf1, 0x41, 0x98, 0x1c, 0xd9, /* 0x0638 */ 0xc0, 0xec, 0x12, 0x95, 0x02, 0x80, 0x01, 0xd4, /* 0x0640 */ 0x40, 0xf0, 0x56, 0xf2, 0x0b, 0x67, 0xfd, 0x7d, /* 0x0648 */ 0x03, 0x99, 0xc4, 0x92, 0x0c, 0x99, 0x96, 0x03, /* 0x0650 */ 0x1c, 0xd9, 0x06, 0x82, 0x41, 0x98, 0x1c, 0xd9, /* 0x0658 */ 0x02, 0x82, 0x42, 0x98, 0x1c, 0xd9, 0x05, 0x82, /* 0x0660 */ 0x0c, 0x69, 0x80, 0x7f, 0x1c, 0xd9, 0x00, 0xb0, /* 0x0668 */ 0x09, 0xa3, 0x00, 0x01, 0xc3, 0xd2, 0x01, 0x94, /* 0x0670 */ 0x0a, 0xb3, 0x04, 0x00, 0x40, 0x42, 0x02, 0x4e, /* 0x0678 */ 0x0f, 0x9f, 0x43, 0xf1, 0x42, 0xa4, 0x1a, 0xd5, /* 0x0680 */ 0x02, 0x80, 0x42, 0x00, 0x88, 0x98, 0x90, 0x9a, /* 0x0688 */ 0x88, 0xda, 0x42, 0x20, 0x08, 0x0b, 0x01, 0x00, /* 0x0690 */ 0x05, 0x92, 0xc5, 0xd2, 0x60, 0xb2, 0x40, 0x42, /* 0x0698 */ 0x02, 0x4e, 0x0f, 0x9f, 0x55, 0xf1, 0x40, 0xf0, /* 0x06a0 */ 0x35, 0xf7, 0xc5, 0x94, 0x0a, 0xb3, 0x10, 0x00, /* 0x06a8 */ 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x5e, 0xf1, /* 0x06b0 */ 0x40, 0xf0, 0x23, 0xf6, 0xc5, 0x96, 0x0b, 0xb3, /* 0x06b8 */ 0x40, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, /* 0x06c0 */ 0x67, 0xf1, 0x40, 0xf0, 0x5d, 0xf5, 0xc5, 0x94, /* 0x06c8 */ 0x0a, 0xb3, 0x01, 0x00, 0x40, 0x42, 0x02, 0x4e, /* 0x06d0 */ 0x0f, 0x9f, 0xc8, 0xf1, 0x13, 0x97, 0x21, 0xbc, /* 0x06d8 */ 0x01, 0xd6, 0x0b, 0xb3, 0x02, 0x00, 0x40, 0x42, /* 0x06e0 */ 0x02, 0x4e, 0x0f, 0x9f, 0x79, 0xf1, 0x40, 0xf0, /* 0x06e8 */ 0x62, 0xfb, 0x01, 0x94, 0x0a, 0xb3, 0x04, 0x00, /* 0x06f0 */ 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x82, 0xf1, /* 0x06f8 */ 0x40, 0xf0, 0x6c, 0xfb, 0x01, 0x96, 0x0b, 0xb3, /* 0x0700 */ 0x01, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, /* 0x0708 */ 0xa2, 0xf1, 0x40, 0xf0, 0xb0, 0xfa, 0x41, 0x92, /* 0x0710 */ 0x19, 0xd3, 0xd5, 0xf7, 0x11, 0x93, 0x03, 0xec, /* 0x0718 */ 0x09, 0x43, 0x40, 0x00, 0x02, 0x5e, 0x0f, 0x9f, /* 0x0720 */ 0x98, 0xf1, 0x40, 0x94, 0x1a, 0xd5, 0xd5, 0xf7, /* 0x0728 */ 0x11, 0x93, 0x00, 0xec, 0x40, 0x42, 0x02, 0x4e, /* 0x0730 */ 0x0f, 0x9f, 0xab, 0xf1, 0x40, 0xf0, 0x38, 0xf2, /* 0x0738 */ 0x0f, 0x9f, 0xab, 0xf1, 0x01, 0x96, 0x0b, 0xb3, /* 0x0740 */ 0x08, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, /* 0x0748 */ 0xab, 0xf1, 0x40, 0xf0, 0x7c, 0xfb, 0x01, 0x94, /* 0x0750 */ 0x0a, 0xb3, 0x10, 0x00, 0x40, 0x42, 0x02, 0x4e, /* 0x0758 */ 0x0f, 0x9f, 0xb4, 0xf1, 0x40, 0xf0, 0x87, 0xfb, /* 0x0760 */ 0x11, 0x93, 0x10, 0xec, 0x42, 0x42, 0x02, 0x5e, /* 0x0768 */ 0x0f, 0x9f, 0xbf, 0xf1, 0x44, 0x96, 0x1b, 0xd7, /* 0x0770 */ 0x0b, 0xbc, 0x0f, 0x9f, 0xc5, 0xf1, 0x41, 0x42, /* 0x0778 */ 0x02, 0x5e, 0x0f, 0x9f, 0xc5, 0xf1, 0x19, 0xd3, /* 0x0780 */ 0x0b, 0xbc, 0x40, 0x92, 0x19, 0xd3, 0x10, 0xec, /* 0x0788 */ 0xc5, 0x94, 0x0a, 0xb3, 0x80, 0x00, 0x40, 0x42, /* 0x0790 */ 0x02, 0x4e, 0x0f, 0x9f, 0x12, 0xf2, 0x13, 0x97, /* 0x0798 */ 0x28, 0xbc, 0x01, 0xd6, 0x0b, 0xb3, 0x40, 0x00, /* 0x07a0 */ 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0xda, 0xf1, /* 0x07a8 */ 0x40, 0xf0, 0x18, 0xf7, 0x01, 0x94, 0x0a, 0xb3, /* 0x07b0 */ 0x02, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, /* 0x07b8 */ 0xed, 0xf1, 0x40, 0xf0, 0xc4, 0xee, 0x40, 0xf0, /* 0x07c0 */ 0x8f, 0xfb, 0x40, 0xf0, 0x1b, 0xf2, 0x40, 0x96, /* 0x07c8 */ 0x1b, 0xd7, 0x00, 0xec, 0x41, 0x92, 0x19, 0xd3, /* 0x07d0 */ 0xd8, 0xf7, 0x01, 0x94, 0x0a, 0xb3, 0x04, 0x00, /* 0x07d8 */ 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x09, 0xf2, /* 0x07e0 */ 0x40, 0xf0, 0x9e, 0xfb, 0x09, 0x63, 0x00, 0x44, /* 0x07e8 */ 0x01, 0x97, 0xc3, 0x94, 0x48, 0xa4, 0xc1, 0xd4, /* 0x07f0 */ 0x00, 0xee, 0x40, 0x92, 0x19, 0xd3, 0x12, 0x95, /* 0x07f8 */ 0x19, 0xd3, 0x10, 0x95, 0x19, 0xd3, 0x02, 0x80, /* 0x0800 */ 0x19, 0xd3, 0x03, 0x82, 0x41, 0x92, 0x19, 0xd3, /* 0x0808 */ 0xd8, 0xf7, 0x01, 0x94, 0x0a, 0xb3, 0x08, 0x00, /* 0x0810 */ 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x12, 0xf2, /* 0x0818 */ 0x40, 0xf0, 0xae, 0xfb, 0x0a, 0x65, 0x00, 0x44, /* 0x0820 */ 0x02, 0x97, 0xc3, 0x92, 0x44, 0xa2, 0xc2, 0xd2, /* 0x0828 */ 0x42, 0x00, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, /* 0x0830 */ 0x08, 0x0b, 0x01, 0x00, 0x09, 0x63, 0x00, 0x40, /* 0x0838 */ 0x19, 0xd3, 0xf2, 0xbd, 0x0a, 0x65, 0xea, 0x43, /* 0x0840 */ 0x02, 0x97, 0xc3, 0x92, 0x44, 0xa2, 0xc2, 0xd2, /* 0x0848 */ 0x0a, 0x65, 0xe9, 0x43, 0x02, 0x97, 0xc3, 0x92, /* 0x0850 */ 0x09, 0xa3, 0x40, 0x00, 0xc2, 0xd2, 0x0a, 0x65, /* 0x0858 */ 0xeb, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xa3, /* 0x0860 */ 0xc0, 0x00, 0xc2, 0xd2, 0x88, 0x98, 0x90, 0x9a, /* 0x0868 */ 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x09, 0x63, /* 0x0870 */ 0x00, 0x80, 0x19, 0xd3, 0xf2, 0xbd, 0x0a, 0x65, /* 0x0878 */ 0xe8, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xa3, /* 0x0880 */ 0xc0, 0x00, 0xc2, 0xd2, 0x0a, 0x65, 0xeb, 0x43, /* 0x0888 */ 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3, 0xbf, 0xff, /* 0x0890 */ 0xc2, 0xd2, 0x0a, 0x65, 0xea, 0x43, 0x02, 0x97, /* 0x0898 */ 0xc3, 0x92, 0x09, 0xb3, 0xfb, 0xff, 0xc2, 0xd2, /* 0x08a0 */ 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, /* 0x08a8 */ 0x01, 0x00, 0x09, 0x93, 0x00, 0x01, 0x19, 0xd3, /* 0x08b0 */ 0x02, 0x80, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, /* 0x08b8 */ 0x08, 0x0b, 0x01, 0x00, 0x09, 0x93, 0x00, 0x09, /* 0x08c0 */ 0x19, 0xd3, 0x02, 0x80, 0x40, 0xf0, 0x56, 0xf2, /* 0x08c8 */ 0x40, 0x92, 0x19, 0xd3, 0x94, 0xec, 0xc8, 0xd2, /* 0x08d0 */ 0x09, 0x93, 0x91, 0xec, 0xc8, 0xd2, 0x40, 0xf0, /* 0x08d8 */ 0x2a, 0xef, 0x42, 0x00, 0x88, 0x98, 0x90, 0x9a, /* 0x08e0 */ 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x40, 0xf0, /* 0x08e8 */ 0x3b, 0xf5, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, /* 0x08f0 */ 0x85, 0xf2, 0x0a, 0x65, 0xfe, 0x7f, 0x02, 0x97, /* 0x08f8 */ 0xc3, 0x92, 0x44, 0xa2, 0xc2, 0xd2, 0x0f, 0x9f, /* 0x0900 */ 0x92, 0xf2, 0x40, 0xf0, 0x94, 0xf2, 0x40, 0x42, /* 0x0908 */ 0x02, 0x5e, 0x0f, 0x9f, 0x92, 0xf2, 0xc8, 0xd2, /* 0x0910 */ 0x09, 0x93, 0x91, 0xec, 0xc8, 0xd2, 0x40, 0xf0, /* 0x0918 */ 0x2a, 0xef, 0x42, 0x00, 0x88, 0x98, 0x90, 0x9a, /* 0x0920 */ 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x11, 0x93, /* 0x0928 */ 0xf1, 0xbd, 0x19, 0xd3, 0xb6, 0xec, 0x11, 0x93, /* 0x0930 */ 0xb4, 0xec, 0x40, 0x42, 0x02, 0x5e, 0x0f, 0x9f, /* 0x0938 */ 0xac, 0xf2, 0x09, 0x63, 0x00, 0x80, 0x01, 0x97, /* 0x0940 */ 0xc3, 0x94, 0x0a, 0x07, 0x07, 0x00, 0xc1, 0xd6, /* 0x0948 */ 0x0a, 0x05, 0x00, 0xa0, 0x1a, 0xd5, 0x96, 0xec, /* 0x0950 */ 0x11, 0x93, 0xb6, 0xec, 0x19, 0xd3, 0x01, 0x80, /* 0x0958 */ 0x0a, 0x65, 0xfe, 0x7f, 0x02, 0x97, 0xc3, 0x92, /* 0x0960 */ 0x41, 0xa2, 0xc2, 0xd2, 0x40, 0x92, 0x88, 0x98, /* 0x0968 */ 0x90, 0x9a, 0x88, 0xda, 0x41, 0x20, 0x08, 0x0b, /* 0x0970 */ 0x01, 0x00, 0x13, 0x97, 0xb4, 0xec, 0x40, 0x46, /* 0x0978 */ 0x02, 0x5e, 0x0f, 0x9f, 0x2c, 0xf3, 0x12, 0x95, /* 0x0980 */ 0x96, 0xec, 0x0a, 0x03, 0x07, 0x00, 0xc1, 0x92, /* 0x0988 */ 0xc2, 0xd2, 0x11, 0x93, 0x96, 0xec, 0x09, 0x05, /* 0x0990 */ 0x01, 0x00, 0x48, 0x02, 0xc1, 0x92, 0xc2, 0xd2, /* 0x0998 */ 0x11, 0x93, 0x96, 0xec, 0x4e, 0x02, 0xc1, 0x94, /* 0x09a0 */ 0xc5, 0xd6, 0xc5, 0x92, 0x11, 0x07, 0x96, 0xec, /* 0x09a8 */ 0x0b, 0x03, 0x0f, 0x00, 0xc1, 0x98, 0x46, 0x06, /* 0x09b0 */ 0x7a, 0x93, 0x79, 0x93, 0x5c, 0x95, 0x5a, 0x95, /* 0x09b8 */ 0x02, 0xa3, 0xc3, 0xd2, 0x04, 0x95, 0xc5, 0x96, /* 0x09c0 */ 0x41, 0x06, 0xc5, 0xd6, 0x42, 0x46, 0x02, 0x9e, /* 0x09c8 */ 0x0f, 0x9f, 0xd5, 0xf2, 0x11, 0x93, 0x96, 0xec, /* 0x09d0 */ 0x09, 0x05, 0x05, 0x00, 0x41, 0x02, 0xc1, 0x92, /* 0x09d8 */ 0xc2, 0xd2, 0x11, 0x93, 0x96, 0xec, 0xc1, 0x92, /* 0x09e0 */ 0x09, 0xb5, 0x1f, 0x00, 0x43, 0x44, 0x02, 0x8e, /* 0x09e8 */ 0x0f, 0x9f, 0x02, 0xf3, 0x40, 0x44, 0x02, 0x4e, /* 0x09f0 */ 0x0f, 0x9f, 0x03, 0xf3, 0x0a, 0x05, 0xff, 0xff, /* 0x09f8 */ 0x0f, 0x9f, 0x03, 0xf3, 0x43, 0x94, 0x11, 0x93, /* 0x0a00 */ 0x96, 0xec, 0x42, 0x02, 0xc1, 0xd4, 0x11, 0x93, /* 0x0a08 */ 0x96, 0xec, 0x49, 0x02, 0xc1, 0x92, 0x19, 0xd3, /* 0x0a10 */ 0xb4, 0xec, 0x09, 0x05, 0xf2, 0xff, 0x1a, 0xd5, /* 0x0a18 */ 0x92, 0xec, 0x09, 0x43, 0xd0, 0x07, 0x02, 0x9e, /* 0x0a20 */ 0x0f, 0x9f, 0x2c, 0xf3, 0x11, 0x93, 0xdc, 0xf7, /* 0x0a28 */ 0x41, 0x02, 0x19, 0xd3, 0xdc, 0xf7, 0x11, 0x93, /* 0x0a30 */ 0xdb, 0xf7, 0x09, 0xa3, 0x40, 0x00, 0x19, 0xd3, /* 0x0a38 */ 0xdb, 0xf7, 0x09, 0x63, 0x00, 0x80, 0x01, 0x95, /* 0x0a40 */ 0xc2, 0x94, 0x1a, 0xd5, 0xb5, 0xec, 0x40, 0x96, /* 0x0a48 */ 0x1b, 0xd7, 0xb4, 0xec, 0x0f, 0x9f, 0x92, 0xf3, /* 0x0a50 */ 0x11, 0x93, 0x92, 0xec, 0x12, 0x95, 0xb6, 0xec, /* 0x0a58 */ 0x02, 0x43, 0x02, 0x8e, 0x0f, 0x9f, 0x7a, 0xf3, /* 0x0a60 */ 0x02, 0x0e, 0x0f, 0x9f, 0x4d, 0xf3, 0x11, 0x93, /* 0x0a68 */ 0xdc, 0xf7, 0x41, 0x02, 0x19, 0xd3, 0xdc, 0xf7, /* 0x0a70 */ 0x11, 0x93, 0xdb, 0xf7, 0x09, 0xa3, 0x80, 0x00, /* 0x0a78 */ 0x19, 0xd3, 0xdb, 0xf7, 0x09, 0x63, 0x00, 0x80, /* 0x0a80 */ 0x01, 0x95, 0xc2, 0x94, 0x1a, 0xd5, 0xb5, 0xec, /* 0x0a88 */ 0x40, 0x96, 0x1b, 0xd7, 0xb4, 0xec, 0x0f, 0x9f, /* 0x0a90 */ 0x92, 0xf3, 0x11, 0x93, 0x03, 0x80, 0x09, 0xb3, /* 0x0a98 */ 0x00, 0x40, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, /* 0x0aa0 */ 0x5f, 0xf3, 0x11, 0x93, 0xc0, 0xec, 0x40, 0x42, /* 0x0aa8 */ 0x02, 0x5e, 0x0f, 0x9f, 0x5f, 0xf3, 0x40, 0xf0, /* 0x0ab0 */ 0xa6, 0xf3, 0x0f, 0x9f, 0x94, 0xf3, 0x41, 0x92, /* 0x0ab8 */ 0xc8, 0xd2, 0x0a, 0x95, 0x91, 0xec, 0xc8, 0xd4, /* 0x0ac0 */ 0x40, 0xf0, 0x2a, 0xef, 0x42, 0x00, 0x11, 0x93, /* 0x0ac8 */ 0xc0, 0xec, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, /* 0x0ad0 */ 0x72, 0xf3, 0x42, 0x96, 0x1b, 0xd7, 0xc0, 0xec, /* 0x0ad8 */ 0x0f, 0x9f, 0x94, 0xf3, 0x0a, 0x65, 0xfe, 0x7f, /* 0x0ae0 */ 0x02, 0x97, 0xc3, 0x92, 0x42, 0xa2, 0xc2, 0xd2, /* 0x0ae8 */ 0x0f, 0x9f, 0x94, 0xf3, 0x12, 0x45, 0x03, 0xec, /* 0x0af0 */ 0x02, 0x4e, 0x0f, 0x9f, 0x8c, 0xf3, 0x11, 0x93, /* 0x0af8 */ 0xdc, 0xf7, 0x41, 0x02, 0x19, 0xd3, 0xdc, 0xf7, /* 0x0b00 */ 0x11, 0x93, 0xdb, 0xf7, 0x09, 0xa3, 0x00, 0x08, /* 0x0b08 */ 0x19, 0xd3, 0xdb, 0xf7, 0x1a, 0xd5, 0x92, 0xec, /* 0x0b10 */ 0x11, 0x93, 0x92, 0xec, 0x19, 0x25, 0x92, 0xec, /* 0x0b18 */ 0x09, 0x63, 0x00, 0x80, 0x19, 0xd3, 0xf2, 0xbd, /* 0x0b20 */ 0x41, 0x00, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, /* 0x0b28 */ 0x08, 0x0b, 0x01, 0x00, 0x40, 0xf0, 0xa6, 0xf3, /* 0x0b30 */ 0x40, 0x92, 0xc8, 0xd2, 0x09, 0x93, 0x91, 0xec, /* 0x0b38 */ 0xc8, 0xd2, 0x40, 0xf0, 0x2a, 0xef, 0x42, 0x00, /* 0x0b40 */ 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, /* 0x0b48 */ 0x01, 0x00, 0x11, 0x93, 0xd7, 0xf7, 0x40, 0x42, /* 0x0b50 */ 0x02, 0x4e, 0x0f, 0x9f, 0xb6, 0xf3, 0x0a, 0x65, /* 0x0b58 */ 0xbc, 0x69, 0x02, 0x97, 0xc3, 0x92, 0x09, 0x83, /* 0x0b60 */ 0x00, 0x02, 0xc2, 0xd2, 0x11, 0x93, 0x03, 0x80, /* 0x0b68 */ 0x09, 0xb3, 0x00, 0x40, 0x40, 0x42, 0x02, 0x5e, /* 0x0b70 */ 0x0f, 0x9f, 0xc9, 0xf3, 0x11, 0x93, 0xdc, 0xf7, /* 0x0b78 */ 0x41, 0x02, 0x19, 0xd3, 0xdc, 0xf7, 0x11, 0x93, /* 0x0b80 */ 0xdb, 0xf7, 0x09, 0xa3, 0x00, 0x20, 0x19, 0xd3, /* 0x0b88 */ 0xdb, 0xf7, 0x11, 0x93, 0xb5, 0xec, 0x19, 0xd3, /* 0x0b90 */ 0x04, 0x80, 0x12, 0x95, 0xb4, 0xec, 0x1a, 0xd5, /* 0x0b98 */ 0x05, 0x80, 0x09, 0x63, 0x00, 0x80, 0x01, 0x97, /* 0x0ba0 */ 0xc3, 0x96, 0x1b, 0xd7, 0xb5, 0xec, 0x40, 0x94, /* 0x0ba8 */ 0x1a, 0xd5, 0xb4, 0xec, 0x19, 0xd3, 0xf2, 0xbd, /* 0x0bb0 */ 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, /* 0x0bb8 */ 0x01, 0x00, 0x09, 0x93, 0x96, 0x03, 0x19, 0xd3, /* 0x0bc0 */ 0x06, 0x82, 0x09, 0x93, 0x00, 0x01, 0x19, 0xd3, /* 0x0bc8 */ 0x03, 0x82, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, /* 0x0bd0 */ 0x47, 0x20, 0x08, 0x0b, 0x01, 0x00, 0x11, 0x93, /* 0x0bd8 */ 0x01, 0x82, 0xc5, 0xd2, 0x40, 0x94, 0x01, 0xd4, /* 0x0be0 */ 0x13, 0x97, 0xb8, 0xec, 0x02, 0xd6, 0x03, 0x95, /* 0x0be8 */ 0x0c, 0x99, 0xbb, 0xec, 0x04, 0x05, 0x13, 0x97, /* 0x0bf0 */ 0x03, 0xec, 0x01, 0x27, 0x02, 0x99, 0xc4, 0x92, /* 0x0bf8 */ 0x03, 0x03, 0xc2, 0xd2, 0x14, 0x99, 0xba, 0xec, /* 0x0c00 */ 0x03, 0x09, 0x1c, 0xd9, 0xba, 0xec, 0x12, 0x95, /* 0x0c08 */ 0x04, 0x82, 0x0a, 0xb3, 0x02, 0x00, 0x40, 0x42, /* 0x0c10 */ 0x02, 0x4e, 0x0f, 0x9f, 0x29, 0xf5, 0x01, 0x92, /* 0x0c18 */ 0x03, 0xd2, 0x0a, 0xa3, 0x02, 0x00, 0x19, 0xd3, /* 0x0c20 */ 0x04, 0x82, 0x02, 0x96, 0x0b, 0x05, 0x01, 0x00, /* 0x0c28 */ 0x1a, 0xd5, 0xb8, 0xec, 0xc5, 0x92, 0x43, 0x42, /* 0x0c30 */ 0x02, 0x9e, 0x0f, 0x9f, 0x37, 0xf4, 0x42, 0x44, /* 0x0c38 */ 0x02, 0x8e, 0x0f, 0x9f, 0x37, 0xf4, 0x11, 0x93, /* 0x0c40 */ 0xbf, 0xec, 0x40, 0x42, 0x02, 0x5e, 0x0f, 0x9f, /* 0x0c48 */ 0x37, 0xf4, 0x0c, 0x49, 0xd3, 0x08, 0x02, 0x8e, /* 0x0c50 */ 0x0f, 0x9f, 0x37, 0xf4, 0x11, 0x63, 0x07, 0x82, /* 0x0c58 */ 0x11, 0xa3, 0x07, 0x82, 0x71, 0x93, 0x79, 0x93, /* 0x0c60 */ 0x79, 0x93, 0x79, 0x93, 0x03, 0xd2, 0xc5, 0x94, /* 0x0c68 */ 0x0a, 0xb5, 0xfc, 0xff, 0x04, 0xd4, 0x03, 0x96, /* 0x0c70 */ 0x40, 0x46, 0x02, 0x5e, 0x0f, 0x9f, 0x46, 0xf4, /* 0x0c78 */ 0x11, 0x93, 0xb8, 0xec, 0x41, 0x42, 0x02, 0x8e, /* 0x0c80 */ 0x0f, 0x9f, 0x4d, 0xf4, 0xc5, 0x98, 0x0c, 0x03, /* 0x0c88 */ 0xff, 0xff, 0x42, 0x42, 0x02, 0x8e, 0x0f, 0x9f, /* 0x0c90 */ 0x74, 0xf4, 0x0a, 0x95, 0xbb, 0xec, 0x42, 0x92, /* 0x0c98 */ 0x19, 0xd3, 0xb9, 0xec, 0xc5, 0x96, 0x43, 0x46, /* 0x0ca0 */ 0x02, 0x9e, 0x0f, 0x9f, 0x66, 0xf4, 0x0b, 0x07, /* 0x0ca8 */ 0xfc, 0xff, 0xc5, 0xd6, 0xd2, 0x98, 0x1c, 0xd9, /* 0x0cb0 */ 0xc8, 0xbc, 0xd2, 0x96, 0x1b, 0xd7, 0xca, 0xbc, /* 0x0cb8 */ 0x09, 0x03, 0xff, 0xff, 0x40, 0x42, 0x02, 0x5e, /* 0x0cc0 */ 0x0f, 0x9f, 0x52, 0xf4, 0x19, 0xd3, 0xb9, 0xec, /* 0x0cc8 */ 0x40, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0x72, 0xf4, /* 0x0cd0 */ 0x0a, 0x05, 0xfe, 0xff, 0xca, 0xd2, 0xc2, 0xd2, /* 0x0cd8 */ 0x0f, 0x9f, 0x74, 0xf4, 0x1a, 0xd5, 0x93, 0xec, /* 0x0ce0 */ 0x03, 0x98, 0x40, 0x48, 0x02, 0x5e, 0x0f, 0x9f, /* 0x0ce8 */ 0xa1, 0xf4, 0x11, 0x93, 0xb8, 0xec, 0x41, 0x42, /* 0x0cf0 */ 0x02, 0x9e, 0x0f, 0x9f, 0x84, 0xf4, 0x04, 0x94, /* 0x0cf8 */ 0x48, 0x44, 0x02, 0x4e, 0x0f, 0x9f, 0x8f, 0xf4, /* 0x0d00 */ 0x41, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0xa1, 0xf4, /* 0x0d08 */ 0x11, 0x93, 0x04, 0x82, 0x41, 0xb2, 0x40, 0x42, /* 0x0d10 */ 0x02, 0x4e, 0x0f, 0x9f, 0xa1, 0xf4, 0x41, 0x96, /* 0x0d18 */ 0x01, 0xd6, 0x0a, 0x65, 0xbd, 0x43, 0x02, 0x99, /* 0x0d20 */ 0xc4, 0x92, 0x09, 0xa3, 0x80, 0x00, 0xc2, 0xd2, /* 0x0d28 */ 0x0a, 0x65, 0xe8, 0x43, 0x02, 0x97, 0xc3, 0x92, /* 0x0d30 */ 0x09, 0xb3, 0xbf, 0xff, 0xc2, 0xd2, 0x0f, 0x9f, /* 0x0d38 */ 0xfa, 0xf4, 0xc5, 0x98, 0x43, 0x48, 0x02, 0x9e, /* 0x0d40 */ 0x0f, 0x9f, 0xfa, 0xf4, 0x4f, 0x96, 0x0c, 0xb3, /* 0x0d48 */ 0x01, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, /* 0x0d50 */ 0xae, 0xf4, 0x47, 0x96, 0x11, 0x93, 0xb7, 0xec, /* 0x0d58 */ 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0xd6, 0xf4, /* 0x0d60 */ 0x11, 0x93, 0xb8, 0xec, 0x41, 0x42, 0x02, 0x5e, /* 0x0d68 */ 0x0f, 0x9f, 0xd6, 0xf4, 0x12, 0x95, 0x00, 0x82, /* 0x0d70 */ 0x0a, 0x05, 0xff, 0xaf, 0x05, 0xd4, 0xc8, 0xd6, /* 0x0d78 */ 0xc8, 0xd2, 0x40, 0xf0, 0x7b, 0xf7, 0x42, 0x00, /* 0x0d80 */ 0x05, 0x96, 0xc3, 0x94, 0x01, 0xb5, 0x40, 0x44, /* 0x0d88 */ 0x02, 0x4e, 0x0f, 0x9f, 0xd6, 0xf4, 0x06, 0x98, /* 0x0d90 */ 0x50, 0x98, 0x1c, 0xd9, 0xa2, 0xbc, 0x40, 0x98, /* 0x0d98 */ 0x1c, 0xd9, 0xa2, 0xbc, 0x40, 0x92, 0x03, 0xd2, /* 0x0da0 */ 0x0f, 0x9f, 0xff, 0xf4, 0x03, 0x94, 0x40, 0x44, /* 0x0da8 */ 0x02, 0x5e, 0x0f, 0x9f, 0xe3, 0xf4, 0x0a, 0x65, /* 0x0db0 */ 0x5e, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x48, 0xa2, /* 0x0db8 */ 0xc2, 0xd2, 0x0f, 0x9f, 0xff, 0xf4, 0x11, 0x93, /* 0x0dc0 */ 0xb8, 0xec, 0x0c, 0x99, 0xbb, 0xec, 0x04, 0x03, /* 0x0dc8 */ 0x04, 0x96, 0x13, 0x25, 0x03, 0xec, 0xc1, 0xd4, /* 0x0dd0 */ 0x11, 0x93, 0xba, 0xec, 0x19, 0x05, 0xba, 0xec, /* 0x0dd8 */ 0x1b, 0xd7, 0x01, 0x82, 0x0a, 0x65, 0xfd, 0x7d, /* 0x0de0 */ 0x02, 0x99, 0xc4, 0x92, 0x43, 0xa2, 0xc2, 0xd2, /* 0x0de8 */ 0x41, 0x92, 0x01, 0xd2, 0x03, 0x94, 0x40, 0x44, /* 0x0df0 */ 0x02, 0x5e, 0x0f, 0x9f, 0x13, 0xf5, 0x11, 0x93, /* 0x0df8 */ 0xb9, 0xec, 0x40, 0x42, 0x02, 0x5e, 0x0f, 0x9f, /* 0x0e00 */ 0x0b, 0xf5, 0x19, 0xd3, 0xb8, 0xec, 0x19, 0xd3, /* 0x0e08 */ 0xba, 0xec, 0x19, 0xd3, 0xbb, 0xec, 0x03, 0x96, /* 0x0e10 */ 0x40, 0x46, 0x02, 0x5e, 0x0f, 0x9f, 0x13, 0xf5, /* 0x0e18 */ 0x41, 0x98, 0x1c, 0xd9, 0xb7, 0xec, 0x11, 0x93, /* 0x0e20 */ 0xbf, 0xec, 0x41, 0x42, 0x02, 0x5e, 0x0f, 0x9f, /* 0x0e28 */ 0x24, 0xf5, 0x11, 0x93, 0x00, 0x82, 0x19, 0xd3, /* 0x0e30 */ 0x02, 0x82, 0x0a, 0x65, 0xfd, 0x7d, 0x02, 0x97, /* 0x0e38 */ 0xc3, 0x92, 0x09, 0xa3, 0x00, 0x01, 0xc2, 0xd2, /* 0x0e40 */ 0x40, 0x98, 0x1c, 0xd9, 0xbf, 0xec, 0x0f, 0x9f, /* 0x0e48 */ 0x2c, 0xf5, 0x01, 0x92, 0x19, 0xd3, 0xb7, 0xec, /* 0x0e50 */ 0x01, 0x94, 0x40, 0x44, 0x02, 0x5e, 0x0f, 0x9f, /* 0x0e58 */ 0x38, 0xf5, 0x0a, 0x65, 0xea, 0x43, 0x02, 0x97, /* 0x0e60 */ 0xc3, 0x92, 0x09, 0xb3, 0xfb, 0xff, 0xc2, 0xd2, /* 0x0e68 */ 0x47, 0x00, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, /* 0x0e70 */ 0x08, 0x0b, 0x01, 0x00, 0x12, 0x95, 0x03, 0x80, /* 0x0e78 */ 0x0a, 0xb3, 0x00, 0x40, 0x40, 0x42, 0x02, 0x4e, /* 0x0e80 */ 0x0f, 0x9f, 0x57, 0xf5, 0x0a, 0xb7, 0x00, 0x08, /* 0x0e88 */ 0x40, 0x46, 0x02, 0x5e, 0x0f, 0x9f, 0x5a, 0xf5, /* 0x0e90 */ 0x11, 0x93, 0x03, 0xec, 0x41, 0x02, 0x09, 0xb3, /* 0x0e98 */ 0xfe, 0xff, 0x12, 0x95, 0x07, 0x80, 0x01, 0x45, /* 0x0ea0 */ 0x02, 0x8e, 0x0f, 0x9f, 0x5a, 0xf5, 0x41, 0x92, /* 0x0ea8 */ 0x0f, 0x9f, 0x5b, 0xf5, 0x40, 0x92, 0x88, 0x98, /* 0x0eb0 */ 0x90, 0x9a, 0x88, 0xda, 0x41, 0x20, 0x08, 0x0b, /* 0x0eb8 */ 0x01, 0x00, 0x0a, 0x65, 0xe9, 0x43, 0x02, 0x97, /* 0x0ec0 */ 0xc3, 0x92, 0x09, 0xa3, 0x40, 0x00, 0xc2, 0xd2, /* 0x0ec8 */ 0x13, 0x97, 0x6e, 0xec, 0x0b, 0x47, 0xa0, 0x00, /* 0x0ed0 */ 0x02, 0x5e, 0x0f, 0x9f, 0x86, 0xf5, 0x09, 0x63, /* 0x0ed8 */ 0x08, 0x43, 0x0a, 0x65, 0xff, 0x5f, 0x01, 0x99, /* 0x0ee0 */ 0xc4, 0xd4, 0x0a, 0x95, 0x9b, 0xec, 0xd2, 0x96, /* 0x0ee8 */ 0x1b, 0xd7, 0xfa, 0xbc, 0xd2, 0x96, 0xc4, 0xd6, /* 0x0ef0 */ 0xd2, 0x98, 0x1c, 0xd9, 0xfa, 0xbc, 0xd2, 0x96, /* 0x0ef8 */ 0xc1, 0xd6, 0xc2, 0x94, 0x1a, 0xd5, 0xfa, 0xbc, /* 0x0f00 */ 0x0f, 0x9f, 0xc4, 0xf5, 0x0c, 0x69, 0xff, 0x6f, /* 0x0f08 */ 0x1c, 0xd9, 0xf8, 0xbc, 0x0b, 0x47, 0x10, 0x95, /* 0x0f10 */ 0x02, 0x5e, 0x0f, 0x9f, 0x9e, 0xf5, 0x0a, 0x95, /* 0x0f18 */ 0x6f, 0xec, 0x09, 0x63, 0x06, 0x43, 0x01, 0x99, /* 0x0f20 */ 0xc4, 0xd6, 0xd2, 0x96, 0x1b, 0xd7, 0xf8, 0xbc, /* 0x0f28 */ 0x0c, 0x69, 0xee, 0x6a, 0xc1, 0xd8, 0xc2, 0x94, /* 0x0f30 */ 0x1a, 0xd5, 0xf8, 0xbc, 0x40, 0x92, 0xc5, 0xd2, /* 0x0f38 */ 0x11, 0x43, 0xc1, 0xec, 0x02, 0x0e, 0x0f, 0x9f, /* 0x0f40 */ 0xc1, 0xf5, 0xc5, 0x94, 0x0a, 0x03, 0x71, 0xec, /* 0x0f48 */ 0xc1, 0x94, 0x1a, 0xd5, 0xfa, 0xbc, 0x11, 0x93, /* 0x0f50 */ 0xc0, 0xec, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, /* 0x0f58 */ 0xb3, 0xf5, 0x0a, 0x95, 0x6f, 0xec, 0xc8, 0xd4, /* 0x0f60 */ 0x40, 0xf0, 0x9c, 0xf7, 0x19, 0xd3, 0xf8, 0xbc, /* 0x0f68 */ 0x41, 0x00, 0xc5, 0x96, 0x41, 0x06, 0xc5, 0xd6, /* 0x0f70 */ 0x13, 0x47, 0xc1, 0xec, 0x02, 0x1e, 0x0f, 0x9f, /* 0x0f78 */ 0xa5, 0xf5, 0x40, 0x98, 0x1c, 0xd9, 0xfa, 0xbc, /* 0x0f80 */ 0x40, 0x92, 0x19, 0xd3, 0x6e, 0xec, 0x19, 0xd3, /* 0x0f88 */ 0xc1, 0xec, 0x0a, 0x65, 0x52, 0x43, 0x02, 0x97, /* 0x0f90 */ 0xc3, 0x92, 0x48, 0xa2, 0xc2, 0xd2, 0x0a, 0x65, /* 0x0f98 */ 0xeb, 0x43, 0x02, 0x99, 0xc4, 0x92, 0x09, 0xb3, /* 0x0fa0 */ 0xbf, 0xff, 0xc2, 0xd2, 0x41, 0x00, 0x88, 0x98, /* 0x0fa8 */ 0x90, 0x9a, 0x88, 0xda, 0x43, 0x20, 0x08, 0x0b, /* 0x0fb0 */ 0x01, 0x00, 0x06, 0x92, 0x01, 0xd2, 0x0a, 0x65, /* 0x0fb8 */ 0xf0, 0x6a, 0x0b, 0x97, 0x6f, 0xec, 0x02, 0x99, /* 0x0fc0 */ 0xc4, 0x98, 0xd3, 0xd8, 0x02, 0xd6, 0x0a, 0x03, /* 0x0fc8 */ 0x02, 0x00, 0x01, 0x97, 0xc3, 0x98, 0x02, 0x96, /* 0x0fd0 */ 0xc3, 0xd8, 0x01, 0x96, 0xc1, 0xd6, 0x1a, 0xd5, /* 0x0fd8 */ 0x6e, 0xec, 0xc5, 0x98, 0x14, 0x99, 0x6f, 0xec, /* 0x0fe0 */ 0xc2, 0xd8, 0x43, 0x00, 0x88, 0x98, 0x90, 0x9a, /* 0x0fe8 */ 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x40, 0x92, /* 0x0ff0 */ 0xc8, 0xd2, 0x40, 0xf0, 0xd9, 0xf5, 0x41, 0x00, /* 0x0ff8 */ 0x11, 0x93, 0xc0, 0xec, 0x40, 0x42, 0x02, 0x4e, /* 0x1000 */ 0x0f, 0x9f, 0x13, 0xf6, 0x42, 0x42, 0x02, 0x5e, /* 0x1008 */ 0x0f, 0x9f, 0x10, 0xf6, 0x0a, 0x65, 0xfe, 0x7f, /* 0x1010 */ 0x02, 0x97, 0xc3, 0x92, 0x42, 0xa2, 0xc2, 0xd2, /* 0x1018 */ 0x40, 0x92, 0x19, 0xd3, 0xc0, 0xec, 0x0a, 0x65, /* 0x1020 */ 0xeb, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xa3, /* 0x1028 */ 0xc0, 0x00, 0xc2, 0xd2, 0x0a, 0x65, 0xe9, 0x43, /* 0x1030 */ 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3, 0xbf, 0xff, /* 0x1038 */ 0xc2, 0xd2, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, /* 0x1040 */ 0x63, 0x20, 0x08, 0x0b, 0x01, 0x00, 0x11, 0x93, /* 0x1048 */ 0xaf, 0xbc, 0x47, 0xb2, 0x59, 0x95, 0x5a, 0x95, /* 0x1050 */ 0x12, 0xa5, 0xbf, 0xbc, 0x0a, 0xb3, 0x01, 0x00, /* 0x1058 */ 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x35, 0xf6, /* 0x1060 */ 0x41, 0x04, 0x05, 0x93, 0x40, 0x96, 0x20, 0xd6, /* 0x1068 */ 0x62, 0x97, 0x0f, 0x9f, 0x44, 0xf6, 0x14, 0x99, /* 0x1070 */ 0xfc, 0xbc, 0xd1, 0xd8, 0x14, 0x99, 0xfe, 0xbc, /* 0x1078 */ 0xd1, 0xd8, 0x20, 0x98, 0x42, 0x08, 0x20, 0xd8, /* 0x1080 */ 0x20, 0x98, 0x03, 0x49, 0x02, 0x1e, 0x0f, 0x9f, /* 0x1088 */ 0x3b, 0xf6, 0xc5, 0x92, 0x62, 0x42, 0x02, 0x4e, /* 0x1090 */ 0x0f, 0x9f, 0x5d, 0xf6, 0x02, 0x8e, 0x0f, 0x9f, /* 0x1098 */ 0x57, 0xf6, 0x61, 0x42, 0x02, 0x4e, 0x0f, 0x9f, /* 0x10a0 */ 0x81, 0xf6, 0x0f, 0x9f, 0xae, 0xf6, 0x63, 0x42, /* 0x10a8 */ 0x02, 0x4e, 0x0f, 0x9f, 0xa4, 0xf6, 0x0f, 0x9f, /* 0x10b0 */ 0xae, 0xf6, 0x0d, 0x03, 0x01, 0x00, 0x0c, 0x99, /* 0x10b8 */ 0x71, 0xec, 0x0b, 0x05, 0xff, 0xff, 0x40, 0x96, /* 0x10c0 */ 0x0f, 0x9f, 0x6a, 0xf6, 0xd1, 0x96, 0xd4, 0xd6, /* 0x10c8 */ 0x20, 0x96, 0x41, 0x06, 0x20, 0xd6, 0x02, 0x47, /* 0x10d0 */ 0x02, 0x1e, 0x0f, 0x9f, 0x66, 0xf6, 0x1a, 0xd5, /* 0x10d8 */ 0xc1, 0xec, 0x0a, 0x65, 0xeb, 0x43, 0x02, 0x99, /* 0x10e0 */ 0xc4, 0x92, 0x09, 0xa3, 0xc0, 0x00, 0xc2, 0xd2, /* 0x10e8 */ 0x0a, 0x65, 0xe9, 0x43, 0x02, 0x97, 0xc3, 0x92, /* 0x10f0 */ 0x09, 0xb3, 0xbf, 0xff, 0xc2, 0xd2, 0x0f, 0x9f, /* 0x10f8 */ 0xae, 0xf6, 0x0a, 0x03, 0xfe, 0xff, 0x61, 0x95, /* 0x1100 */ 0x40, 0x98, 0x20, 0xd8, 0x02, 0x49, 0x02, 0x0e, /* 0x1108 */ 0x0f, 0x9f, 0xae, 0xf6, 0x0d, 0x03, 0x01, 0x00, /* 0x1110 */ 0x21, 0xd2, 0x20, 0x92, 0x05, 0x03, 0x42, 0x02, /* 0x1118 */ 0xc8, 0xd2, 0x21, 0x96, 0xc3, 0x92, 0x42, 0x06, /* 0x1120 */ 0x21, 0xd6, 0xc8, 0xd2, 0x22, 0xd4, 0x40, 0xf0, /* 0x1128 */ 0x01, 0xf1, 0x42, 0x00, 0x20, 0x98, 0x42, 0x08, /* 0x1130 */ 0x20, 0xd8, 0x22, 0x94, 0x02, 0x49, 0x02, 0x1e, /* 0x1138 */ 0x0f, 0x9f, 0x8d, 0xf6, 0x0f, 0x9f, 0xae, 0xf6, /* 0x1140 */ 0x0d, 0x03, 0x03, 0x00, 0xc8, 0xd2, 0x02, 0x92, /* 0x1148 */ 0xc8, 0xd2, 0x01, 0x96, 0xc8, 0xd6, 0x40, 0xf0, /* 0x1150 */ 0xb1, 0xf6, 0x43, 0x00, 0x63, 0x00, 0x88, 0x98, /* 0x1158 */ 0x90, 0x9a, 0x88, 0xda, 0x45, 0x20, 0x08, 0x0b, /* 0x1160 */ 0x01, 0x00, 0x0d, 0x03, 0x08, 0x00, 0x08, 0x94, /* 0x1168 */ 0xc5, 0xd4, 0x09, 0x05, 0x01, 0x00, 0xc2, 0x94, /* 0x1170 */ 0x03, 0xd4, 0x42, 0x02, 0xc1, 0x92, 0x01, 0xd2, /* 0x1178 */ 0x02, 0x97, 0xc5, 0x94, 0x0a, 0x83, 0xff, 0xff, /* 0x1180 */ 0x11, 0xb3, 0x2c, 0x93, 0x09, 0xb3, 0xfb, 0xff, /* 0x1188 */ 0x19, 0xd3, 0x2c, 0x93, 0x03, 0x92, 0x40, 0x42, /* 0x1190 */ 0x02, 0x4e, 0x0f, 0x9f, 0xe4, 0xf6, 0x01, 0x94, /* 0x1198 */ 0xd2, 0x92, 0x19, 0xd3, 0x2c, 0x93, 0x01, 0xd4, /* 0x11a0 */ 0x02, 0x94, 0x12, 0x95, 0x2c, 0x93, 0x44, 0xa4, /* 0x11a8 */ 0x1a, 0xd5, 0x2c, 0x93, 0x0a, 0xb5, 0xfb, 0xff, /* 0x11b0 */ 0x1a, 0xd5, 0x2c, 0x93, 0x0b, 0x07, 0xff, 0xff, /* 0x11b8 */ 0x40, 0x46, 0x02, 0x5e, 0x0f, 0x9f, 0xcf, 0xf6, /* 0x11c0 */ 0x09, 0x63, 0xd4, 0x6c, 0x01, 0x95, 0xc2, 0x96, /* 0x11c8 */ 0xc5, 0x94, 0x02, 0xa7, 0xc1, 0xd6, 0x03, 0x92, /* 0x11d0 */ 0x54, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0xf4, 0xf6, /* 0x11d8 */ 0x0a, 0x83, 0xff, 0xff, 0x1b, 0xb3, 0x2c, 0x93, /* 0x11e0 */ 0x45, 0x00, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, /* 0x11e8 */ 0x08, 0x0b, 0x01, 0x00, 0x09, 0x63, 0x00, 0x40, /* 0x11f0 */ 0x19, 0xd3, 0xf2, 0xbd, 0x40, 0xf0, 0x3b, 0xf5, /* 0x11f8 */ 0x40, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0x08, 0xf7, /* 0x1200 */ 0x40, 0xf0, 0x94, 0xf2, 0x0f, 0x9f, 0x16, 0xf7, /* 0x1208 */ 0x40, 0x96, 0xc8, 0xd6, 0x09, 0x93, 0x91, 0xec, /* 0x1210 */ 0xc8, 0xd2, 0x40, 0xf0, 0x2a, 0xef, 0x0a, 0x65, /* 0x1218 */ 0xfe, 0x7f, 0x02, 0x97, 0xc3, 0x92, 0x44, 0xa2, /* 0x1220 */ 0xc2, 0xd2, 0x42, 0x00, 0x88, 0x98, 0x90, 0x9a, /* 0x1228 */ 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x0a, 0x65, /* 0x1230 */ 0xe8, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xa3, /* 0x1238 */ 0x40, 0x00, 0xc2, 0xd2, 0x0a, 0x65, 0xea, 0x43, /* 0x1240 */ 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3, 0xfb, 0xff, /* 0x1248 */ 0xc2, 0xd2, 0x40, 0x92, 0x19, 0xd3, 0x2d, 0xbc, /* 0x1250 */ 0x0a, 0x65, 0xd8, 0x43, 0x02, 0x97, 0xc3, 0x92, /* 0x1258 */ 0x09, 0xb3, 0xbf, 0xff, 0xc2, 0xd2, 0x88, 0x98, /* 0x1260 */ 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, /* 0x1268 */ 0x09, 0x63, 0xea, 0x43, 0x01, 0x97, 0xc3, 0x94, /* 0x1270 */ 0x44, 0xa4, 0xc1, 0xd4, 0x11, 0x93, 0xb9, 0xec, /* 0x1278 */ 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x6f, 0xf7, /* 0x1280 */ 0x12, 0x95, 0x93, 0xec, 0x0b, 0x67, 0x36, 0x43, /* 0x1288 */ 0xd2, 0x98, 0x1c, 0xd9, 0xc8, 0xbc, 0xd2, 0x98, /* 0x1290 */ 0x03, 0x93, 0xc1, 0xd8, 0x11, 0x93, 0xb9, 0xec, /* 0x1298 */ 0x09, 0x03, 0xff, 0xff, 0x19, 0xd3, 0xb9, 0xec, /* 0x12a0 */ 0x40, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0x48, 0xf7, /* 0x12a8 */ 0x19, 0xd3, 0xb8, 0xec, 0x19, 0xd3, 0xba, 0xec, /* 0x12b0 */ 0x0a, 0x05, 0xfe, 0xff, 0xca, 0xd2, 0xca, 0xd2, /* 0x12b8 */ 0xc2, 0xd2, 0x0a, 0x65, 0x5e, 0x43, 0x02, 0x97, /* 0x12c0 */ 0xc3, 0x92, 0x48, 0xa2, 0xc2, 0xd2, 0x0a, 0x65, /* 0x12c8 */ 0xea, 0x43, 0x02, 0x99, 0xc4, 0x92, 0x09, 0xb3, /* 0x12d0 */ 0xfb, 0xff, 0x0f, 0x9f, 0x78, 0xf7, 0x11, 0x93, /* 0x12d8 */ 0x03, 0xec, 0x19, 0xd3, 0x01, 0x82, 0x0a, 0x65, /* 0x12e0 */ 0xfd, 0x7d, 0x02, 0x97, 0xc3, 0x92, 0x43, 0xa2, /* 0x12e8 */ 0xc2, 0xd2, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, /* 0x12f0 */ 0x08, 0x0b, 0x01, 0x00, 0x03, 0x92, 0x04, 0x96, /* 0x12f8 */ 0x0d, 0x5e, 0x50, 0x46, 0x02, 0x0e, 0x40, 0x92, /* 0x1300 */ 0x09, 0xee, 0x44, 0x46, 0x04, 0x0e, 0x59, 0x93, /* 0x1308 */ 0x44, 0x26, 0x04, 0x5e, 0x46, 0xee, 0x41, 0x93, /* 0x1310 */ 0x41, 0x26, 0x43, 0x4e, 0x88, 0x98, 0x90, 0x9a, /* 0x1318 */ 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x40, 0xf0, /* 0x1320 */ 0xb1, 0xfe, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, /* 0x1328 */ 0x08, 0x0b, 0x01, 0x00, 0x88, 0x98, 0x90, 0x9a, /* 0x1330 */ 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x03, 0x94, /* 0x1338 */ 0x1a, 0xd5, 0xa3, 0xf7, 0x11, 0x93, 0x00, 0x90, /* 0x1340 */ 0x88, 0x98, 0x90, 0x9a, 0x1d, 0x00, 0x1a, 0x00, /* 0x1348 */ 0x03, 0x00, 0x03, 0x00, 0x18, 0x00, 0x19, 0x00, /* 0x1350 */ 0x1a, 0x00, 0x1b, 0x00, 0x16, 0x00, 0x21, 0x00, /* 0x1358 */ 0x12, 0x00, 0x09, 0x00, 0x13, 0x00, 0x19, 0x00, /* 0x1360 */ 0x19, 0x00, 0x19, 0x00, 0x21, 0x00, 0x2d, 0x00, /* 0x1368 */ 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1370 */ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1378 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x69, /* 0x1380 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1388 */ 0x5f, 0xf2, 0xcd, 0xf7, 0x00, 0x00, 0x74, 0xf2, /* 0x1390 */ 0xcd, 0xf7, 0x00, 0x00, 0xb9, 0xf2, 0xca, 0xf7, /* 0x1398 */ 0xd1, 0xf7, 0x00, 0x00, 0x97, 0xf3, 0xcd, 0xf7, /* 0x13a0 */ 0x05, 0x46, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x13a8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x13b0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x13b8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x13c0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x13c8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x13d0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x13d8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x13e0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x13e8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x13f0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x13f8 */ }; pwcbsd/usb/if_zydreg.h000644 000423 000000 00000131353 10551670753 015541 0ustar00luigiwheel000000 000000 /* $OpenBSD: if_zydreg.h,v 1.10 2006/07/02 01:04:58 jsg Exp $ */ /* * Copyright (c) 2006 by Florian Stoehr * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * ZyDAS ZD1211 USB WLAN driver */ /* * The ZD1211 maps threee register spaces into the overall USB * address space of 64K words (16-bit values): * * - Control register space (32-bit registers) * - EEPROM register space (16-bit registers) * - Firmware register space (16-bit registers) * * I'll use the masks and type constants below to handle * register filtering in code (require different treatment). */ enum zyd_masks { ZYD_MASK_DESTRANGE = 0xF0000, /* Mask destination range */ ZYD_MASK_ADDR_OFFS = 0x0FFFF /* Mask address offset */ }; enum zyd_ranges { ZYD_RANGE_USB = 0x10000, /* Range: USB */ ZYD_RANGE_CTL = 0x20000, /* Range: Control */ ZYD_RANGE_E2P = 0x40000, /* Range: EEPROM */ ZYD_RANGE_FW = 0x80000 /* Range: Firmware */ }; /* Nomalize to uint32_t */ #define ZYD_MASK_NORM 0x000FFFFFUL /* Mask (detect) destination range */ #define ZYD_GET_RANGE(addr) \ (ZYD_MASK_NORM & ((uint32_t)(addr) & ZYD_MASK_DESTRANGE)) /* Mask register offset (kick the range off) */ #define ZYD_GET_OFFS(addr) \ (ZYD_MASK_NORM & ((uint32_t)(addr) & ZYD_MASK_ADDR_OFFS)) /* Combine range and offset */ #define ZYD_BUILDCOMBO(range, offs) \ (ZYD_GET_RANGE(range) | ZYD_GET_OFFS(offs)) /* Quick set macros for ranges */ #define ZYD_REG_USB(offs) ZYD_BUILDCOMBO(ZYD_RANGE_USB, offs) /* 16-bit addr */ #define ZYD_REG_CTL(offs) ZYD_BUILDCOMBO(ZYD_RANGE_CTL, offs) /* 8-bit addr */ #define ZYD_REG_E2P(offs) ZYD_BUILDCOMBO(ZYD_RANGE_E2P, offs) /* 16-bit addr */ #define ZYD_REG_FW(offs) ZYD_BUILDCOMBO(ZYD_RANGE_FW, offs) /* 16-bit addr */ #define ZYD_CR_GPI_EN ZYD_REG_CTL(0x418) #define ZYD_CR_RADIO_PD ZYD_REG_CTL(0x42C) #define ZYD_CR_RF2948_PD ZYD_REG_CTL(0x42C) #define ZYD_CR_ENABLE_PS_MANUAL_AGC ZYD_REG_CTL(0x43C) #define ZYD_CR_CONFIG_PHILIPS ZYD_REG_CTL(0x440) #define ZYD_CR_SA2400_SER_AP ZYD_REG_CTL(0x444) #define ZYD_CR_I2C_WRITE ZYD_REG_CTL(0x444) #define ZYD_CR_SA2400_SER_RP ZYD_REG_CTL(0x448) #define ZYD_CR_RADIO_PE ZYD_REG_CTL(0x458) #define ZYD_CR_RST_BUS_MASTER ZYD_REG_CTL(0x45C) #define ZYD_CR_RFCFG ZYD_REG_CTL(0x464) #define ZYD_CR_HSTSCHG ZYD_REG_CTL(0x46C) #define ZYD_CR_PHY_ON ZYD_REG_CTL(0x474) #define ZYD_CR_RX_DELAY ZYD_REG_CTL(0x478) #define ZYD_CR_RX_PE_DELAY ZYD_REG_CTL(0x47C) #define ZYD_CR_GPIO_1 ZYD_REG_CTL(0x490) #define ZYD_CR_GPIO_2 ZYD_REG_CTL(0x494) #define ZYD_CR_EnZYD_CRyBufMux ZYD_REG_CTL(0x4A8) #define ZYD_CR_PS_CTRL ZYD_REG_CTL(0x500) #define ZYD_CR_ADDA_PWR_DWN ZYD_REG_CTL(0x504) #define ZYD_CR_ADDA_MBIAS_WARMTIME ZYD_REG_CTL(0x508) #define ZYD_CR_INTERRUPT ZYD_REG_CTL(0x510) #define ZYD_CR_MAC_PS_STATE ZYD_REG_CTL(0x50C) /* * Following three values are in time units (1024us) * Following condition must be met: * atim < tbtt < bcn */ #define ZYD_CR_ATIM_WND_PERIOD ZYD_REG_CTL(0x51C) #define ZYD_CR_BCN_INTERVAL ZYD_REG_CTL(0x520) #define ZYD_CR_PRE_TBTT ZYD_REG_CTL(0x524) /* * MAC registers */ #define ZYD_MAC_MACADDRL ZYD_REG_CTL(0x610) /* MAC address (low) */ #define ZYD_MAC_MACADDRH ZYD_REG_CTL(0x614) /* MAC address (high) */ #define ZYD_MAC_BSSADRL ZYD_REG_CTL(0x618) /* BSS address (low) */ #define ZYD_MAC_BSSADRH ZYD_REG_CTL(0x61C) /* BSS address (high) */ #define ZYD_MAC_BCNCFG ZYD_REG_CTL(0x620) /* BCN configuration */ #define ZYD_MAC_GHTBL ZYD_REG_CTL(0x624) /* Group hash table (low) */ #define ZYD_MAC_GHTBH ZYD_REG_CTL(0x628) /* Group hash table (high) */ #define ZYD_MAC_RX_TIMEOUT ZYD_REG_CTL(0x62C) /* Rx timeout value */ #define ZYD_MAC_BASICRATE ZYD_REG_CTL(0x630) /* Basic rate setting */ #define ZYD_MAC_MANDATORYRATE ZYD_REG_CTL(0x634) /* Mandatory rate setting */ #define ZYD_MAC_RTSCTSRATE ZYD_REG_CTL(0x638) /* RTS CTS rate */ #define ZYD_MAC_BACKOFF_PROTECT ZYD_REG_CTL(0x63C) /* Backoff protection */ #define ZYD_MAC_RX_THRESHOLD ZYD_REG_CTL(0x640) /* Rx threshold */ #define ZYD_MAC_TX_PE_CONTROL ZYD_REG_CTL(0x644) /* Tx_PE control */ #define ZYD_MAC_AFTER_PNP ZYD_REG_CTL(0x648) /* After PnP */ #define ZYD_MAC_RX_PE_DELAY ZYD_REG_CTL(0x64C) /* Rx_pe delay */ #define ZYD_MAC_RX_ADDR2_L ZYD_REG_CTL(0x650) /* RX address2 (low) */ #define ZYD_MAC_RX_ADDR2_H ZYD_REG_CTL(0x654) /* RX address2 (high) */ #define ZYD_MAC_SIFS_ACK_TIME ZYD_REG_CTL(0x658) /* Dynamic SIFS ack time */ #define ZYD_MAC_PHY_DELAY ZYD_REG_CTL(0x660) /* PHY delay */ #define ZYD_MAC_PHY_DELAY2 ZYD_REG_CTL(0x66C) /* PHY delay */ #define ZYD_MAC_BCNFIFO ZYD_REG_CTL(0x670) /* Beacon FIFO I/O port */ #define ZYD_MAC_SNIFFER ZYD_REG_CTL(0x674) /* Sniffer on/off */ #define ZYD_MAC_ENCRYPTION_TYPE ZYD_REG_CTL(0x678) /* Encryption type */ #define ZYD_MAC_RETRY ZYD_REG_CTL(0x67C) /* Retry time */ #define ZYD_MAC_MISC ZYD_REG_CTL(0x680) /* Misc */ #define ZYD_MAC_STMACHINESTAT ZYD_REG_CTL(0x684) /* State machine status */ #define ZYD_MAC_TX_UNDERRUN_CNT ZYD_REG_CTL(0x688) /* TX underrun counter */ #define ZYD_MAC_STOHOSTSETTING ZYD_REG_CTL(0x68C) /* Send to host settings */ #define ZYD_MAC_ACK_EXT ZYD_REG_CTL(0x690) /* Acknowledge extension */ #define ZYD_MAC_BCNFIFOST ZYD_REG_CTL(0x694) /* BCN FIFO set and status */ #define ZYD_MAC_DIFS_EIFS_SIFS ZYD_REG_CTL(0x698) /* DIFS, EIFS & SIFS settings */ #define ZYD_MAC_RX_TIMEOUT_CNT ZYD_REG_CTL(0x69C) /* RX timeout count */ #define ZYD_MAC_RX_TOTAL_FRAME ZYD_REG_CTL(0x6A0) /* RX total frame count */ #define ZYD_MAC_RX_CRC32_CNT ZYD_REG_CTL(0x6A4) /* RX CRC32 frame count */ #define ZYD_MAC_RX_CRC16_CNT ZYD_REG_CTL(0x6A8) /* RX CRC16 frame count */ #define ZYD_MAC_RX_UDEC ZYD_REG_CTL(0x6AC) /* RX unicast decr. error count */ #define ZYD_MAC_RX_OVERRUN_CNT ZYD_REG_CTL(0x6B0) /* RX FIFO overrun count */ #define ZYD_MAC_RX_MDEC ZYD_REG_CTL(0x6BC) /* RX multicast decr. err. cnt. */ #define ZYD_MAC_NAV_TCR ZYD_REG_CTL(0x6C4) /* NAV timer count read */ #define ZYD_MAC_BACKOFF_ST_RD ZYD_REG_CTL(0x6C8) /* Backoff status read */ #define ZYD_MAC_DM_RETRY_CNT_RD ZYD_REG_CTL(0x6CC) /* DM retry count read */ #define ZYD_MAC_RX_ACR ZYD_REG_CTL(0x6D0) /* RX arbitration count read */ #define ZYD_MAC_TX_CCR ZYD_REG_CTL(0x6D4) /* Tx complete count read */ #define ZYD_MAC_TCB_ADDR ZYD_REG_CTL(0x6E8) /* Current PCI process TCP addr */ #define ZYD_MAC_RCB_ADDR ZYD_REG_CTL(0x6EC) /* Next RCB address */ #define ZYD_MAC_CONT_WIN_LIMIT ZYD_REG_CTL(0x6F0) /* Contention window limit */ #define ZYD_MAC_TX_PKT ZYD_REG_CTL(0x6F4) /* Tx total packet count read */ #define ZYD_MAC_DL_CTRL ZYD_REG_CTL(0x6F8) /* Download control */ /* * EEPROM registers */ #define ZYD_E2P_SUBID ZYD_REG_E2P(0x00) /* ? */ #define ZYD_E2P_POD ZYD_REG_E2P(0x02) /* RF/PA types */ #define ZYD_E2P_MAC_ADDR_P1 ZYD_REG_E2P(0x04) /* Part 1 of the MAC address */ #define ZYD_E2P_MAC_ADDR_P2 ZYD_REG_E2P(0x06) /* Part 2 of the MAC address */ #define ZYD_E2P_PWR_CAL_VALUE1 ZYD_REG_E2P(0x08) /* Calibration */ #define ZYD_E2P_PWR_CAL_VALUE2 ZYD_REG_E2P(0x0a) /* Calibration */ #define ZYD_E2P_PWR_CAL_VALUE3 ZYD_REG_E2P(0x0c) /* Calibration */ #define ZYD_E2P_PWR_CAL_VALUE4 ZYD_REG_E2P(0x0e) /* Calibration */ #define ZYD_E2P_PWR_INT_VALUE1 ZYD_REG_E2P(0x10) /* Calibration */ #define ZYD_E2P_PWR_INT_VALUE2 ZYD_REG_E2P(0x12) /* Calibration */ #define ZYD_E2P_PWR_INT_VALUE3 ZYD_REG_E2P(0x14) /* Calibration */ #define ZYD_E2P_PWR_INT_VALUE4 ZYD_REG_E2P(0x16) /* Calibration */ #define ZYD_E2P_ALLOWED_CHANNEL ZYD_REG_E2P(0x18) /* Allowed CH mask, 1 bit each */ #define ZYD_E2P_PHY_REG ZYD_REG_E2P(0x1a) /* PHY registers */ #define ZYD_E2P_DEVICE_VER ZYD_REG_E2P(0x20) /* Device version */ #define ZYD_E2P_36M_CAL_VALUE1 ZYD_REG_E2P(0x28) /* Calibration */ #define ZYD_E2P_36M_CAL_VALUE2 ZYD_REG_E2P(0x2a) /* Calibration */ #define ZYD_E2P_36M_CAL_VALUE3 ZYD_REG_E2P(0x2c) /* Calibration */ #define ZYD_E2P_36M_CAL_VALUE4 ZYD_REG_E2P(0x2e) /* Calibration */ #define ZYD_E2P_11A_INT_VALUE1 ZYD_REG_E2P(0x30) /* Calibration */ #define ZYD_E2P_11A_INT_VALUE2 ZYD_REG_E2P(0x32) /* Calibration */ #define ZYD_E2P_11A_INT_VALUE3 ZYD_REG_E2P(0x34) /* Calibration */ #define ZYD_E2P_11A_INT_VALUE4 ZYD_REG_E2P(0x36) /* Calibration */ #define ZYD_E2P_48M_CAL_VALUE1 ZYD_REG_E2P(0x38) /* Calibration */ #define ZYD_E2P_48M_CAL_VALUE2 ZYD_REG_E2P(0x3a) /* Calibration */ #define ZYD_E2P_48M_CAL_VALUE3 ZYD_REG_E2P(0x3c) /* Calibration */ #define ZYD_E2P_48M_CAL_VALUE4 ZYD_REG_E2P(0x3e) /* Calibration */ #define ZYD_E2P_48M_INT_VALUE1 ZYD_REG_E2P(0x40) /* Calibration */ #define ZYD_E2P_48M_INT_VALUE2 ZYD_REG_E2P(0x42) /* Calibration */ #define ZYD_E2P_48e_INT_VALUE3 ZYD_REG_E2P(0x44) /* Calibration */ #define ZYD_E2P_48M_INT_VALUE4 ZYD_REG_E2P(0x46) /* Calibration */ #define ZYD_E2P_54M_CAL_VALUE1 ZYD_REG_E2P(0x48) /* Calibration */ #define ZYD_E2P_54M_CAL_VALUE2 ZYD_REG_E2P(0x4a) /* Calibration */ #define ZYD_E2P_54M_CAL_VALUE3 ZYD_REG_E2P(0x4c) /* Calibration */ #define ZYD_E2P_54M_CAL_VALUE4 ZYD_REG_E2P(0x4e) /* Calibration */ #define ZYD_E2P_54M_INT_VALUE1 ZYD_REG_E2P(0x50) /* Calibration */ #define ZYD_E2P_54M_INT_VALUE2 ZYD_REG_E2P(0x52) /* Calibration */ #define ZYD_E2P_54M_INT_VALUE3 ZYD_REG_E2P(0x54) /* Calibration */ #define ZYD_E2P_54M_INT_VALUE4 ZYD_REG_E2P(0x56) /* Calibration */ /* * Firmware registers (all 16-bit) */ #define ZYD_FW_FIRMWARE_VER ZYD_REG_FW(0) /* Firmware version */ #define ZYD_FW_USB_SPEED ZYD_REG_FW(1) /* USB speed (!=0 if highspeed) */ #define ZYD_FW_FIX_TX_RATE ZYD_REG_FW(2) /* Fixed TX rate */ #define ZYD_FW_LINK_STATUS ZYD_REG_FW(3) /* LED control? */ #define ZYD_FW_SOFT_RESET ZYD_REG_FW(4) /* LED control? */ #define ZYD_FW_FLASH_CHK ZYD_REG_FW(5) /* LED control? */ /* * Some addresses */ #define ZYD_CTRL_START_ADDR 0x9000 /* Control registers start */ #define ZYD_FIRMWARE_START_ADDR 0xEE00 /* !! NEWER devices only! */ #define ZYD_FIRMWARE_OLD_ADDR 0xEC00 /* Devices prior to 43.30 */ #define ZYD_FIRMWARE_BASE_ADDR 0xEE1D /* Firmware base addr offset */ #define ZYD_E2P_START_HEAD 0xF800 /* EEPROM start (head) */ #define ZYD_E2P_START_ADDR 0xF817 /* EEPROM registers start */ /* * Firmware uploader - requests and ids */ #define ZYD_FIRMDOWN_REQ 0x40 /* Firmware dl request type */ #define ZYD_FIRMDOWN_ID 0x30 /* Firmware dl ID */ #define ZYD_FIRMSTAT_REQ 0xC0 /* Firmware stat query type */ #define ZYD_FIRMSTAT_ID 0x31 /* Firmware stat query ID */ /* * Firmware uploader - status */ #define ZYD_FIRMWAREUP_OK 0x01 /* Firmware upload OK */ #define ZYD_FIRMWAREUP_FAILURE 0x80 /* Firmware upload failure */ /* * USB stuff */ #define ZYD_CONFIG_NO 1 /* USB configuration */ #define ZYD_IFACE_IDX 0 /* Interface index */ #define ZYD_INTR_BUF_SIZE 64 /* bytes */ /* * RFs */ #define ZYD_RF_UW2451 0x2 #define ZYD_RF_UCHIP 0x3 #define ZYD_RF_AL2230 0x4 #define ZYD_RF_AL7230B 0x5 /* a,b,g */ #define ZYD_RF_THETA 0x6 #define ZYD_RF_AL2210 0x7 #define ZYD_RF_MAXIM_NEW 0x8 #define ZYD_RF_GCT 0x9 #define ZYD_RF_PV2000 0xa #define ZYD_RF_RALINK 0xb #define ZYD_RF_INTERSIL 0xc #define ZYD_RF_RFMD 0xd #define ZYD_RF_MAXIM_NEW2 0xe #define ZYD_RF_PHILIPS 0xf /* * Channal stuff, including domains */ #define ZYD_RF_CHANNEL(ch) [(ch)-1] #define ZYD_REGDOMAIN_FCC 0x10 #define ZYD_REGDOMAIN_IC 0x20 #define ZYD_REGDOMAIN_ETSI 0x30 #define ZYD_REGDOMAIN_SPAIN 0x31 #define ZYD_REGDOMAIN_FRANCE 0x32 #define ZYD_REGDOMAIN_JAPAN_ADD 0x40 #define ZYD_REGDOMAIN_JAPAN 0x41 /* * Register access responses */ #define ZYD_CRS_IORDRSP 0x0190 /* Resonse for IORDREQ */ #define ZYD_CRS_MACINTERRUPT 0x0190 /* Interrupt notification */ #define ZYD_CRS_RETRYSTATUS 0x01A0 /* Report retry fail status */ /* * Register access commands */ #define ZYD_CMD_IOWRREQ 0x0021 /* Request to write register */ #define ZYD_CMD_IORDREQ 0x0022 /* Request to read register */ #define ZYD_CMD_RFCFGREQ 0x0023 /* Write config to RF regs */ /* * RF access */ #define ZYD_RF_IF_LE 2 #define ZYD_RF_CLK 4 #define ZYD_RF_DATA 8 #define ZYD_RF_REG_BITS 6 #define ZYD_RF_VALUE_BITS 18 #define ZYD_RF_RV_BITS (ZYD_RF_REG_BITS + ZYD_RF_VALUE_BITS) /* * Various */ #define ZYD_ALLOWED_DEV_VERSION 0x4330 /* We only allow 43.30 devices */ #define ZYD_AP_RX_FILTER 0x0400FEFF #define ZYD_STA_RX_FILTER 0x0000FFFF #define ZYD_HWINT_ENABLED 0x004f0000 #define ZYD_HWINT_DISABLED 0 #define ZYD_E2P_PWR_INT_GUARD 8 #define ZYD_E2P_CHANNEL_COUNT 14 /* 1,2,5.5,11,6,12,24 */ #define ZYD_DEFAULT_MAND_RATES 0x150f /* * 0x80 set = PHY controlled by MAC, 00 = controlled by host * Affected register is 0x680 (ZYD_MAC_MISC) */ #define ZYD_UNLOCK_PHY_REGS 0x0080 /* Disallow PHY write access */ /* 8-bit hardware registers */ #define ZYD_CR0 ZYD_REG_CTL(0x0000) #define ZYD_CR1 ZYD_REG_CTL(0x0004) #define ZYD_CR2 ZYD_REG_CTL(0x0008) #define ZYD_CR3 ZYD_REG_CTL(0x000C) #define ZYD_CR5 ZYD_REG_CTL(0x0010) /* bit 5: if set short preamble used * bit 6: filter band - Japan channel 14 on, else off */ #define ZYD_CR6 ZYD_REG_CTL(0x0014) #define ZYD_CR7 ZYD_REG_CTL(0x0018) #define ZYD_CR8 ZYD_REG_CTL(0x001C) #define ZYD_CR4 ZYD_REG_CTL(0x0020) #define ZYD_CR9 ZYD_REG_CTL(0x0024) /* bit 2: antenna switch (together with ZYD_CR10) */ #define ZYD_CR10 ZYD_REG_CTL(0x0028) /* bit 1: antenna switch (together with ZYD_CR9) * RF2959 controls with ZYD_CR11 radion on and off */ #define ZYD_CR11 ZYD_REG_CTL(0x002C) /* bit 6: TX power control for OFDM * RF2959 controls with ZYD_CR10 radio on and off */ #define ZYD_CR12 ZYD_REG_CTL(0x0030) #define ZYD_CR13 ZYD_REG_CTL(0x0034) #define ZYD_CR14 ZYD_REG_CTL(0x0038) #define ZYD_CR15 ZYD_REG_CTL(0x003C) #define ZYD_CR16 ZYD_REG_CTL(0x0040) #define ZYD_CR17 ZYD_REG_CTL(0x0044) #define ZYD_CR18 ZYD_REG_CTL(0x0048) #define ZYD_CR19 ZYD_REG_CTL(0x004C) #define ZYD_CR20 ZYD_REG_CTL(0x0050) #define ZYD_CR21 ZYD_REG_CTL(0x0054) #define ZYD_CR22 ZYD_REG_CTL(0x0058) #define ZYD_CR23 ZYD_REG_CTL(0x005C) #define ZYD_CR24 ZYD_REG_CTL(0x0060) /* CCA threshold */ #define ZYD_CR25 ZYD_REG_CTL(0x0064) #define ZYD_CR26 ZYD_REG_CTL(0x0068) #define ZYD_CR27 ZYD_REG_CTL(0x006C) #define ZYD_CR28 ZYD_REG_CTL(0x0070) #define ZYD_CR29 ZYD_REG_CTL(0x0074) #define ZYD_CR30 ZYD_REG_CTL(0x0078) #define ZYD_CR31 ZYD_REG_CTL(0x007C) /* TX power control for RF in CCK mode */ #define ZYD_CR32 ZYD_REG_CTL(0x0080) #define ZYD_CR33 ZYD_REG_CTL(0x0084) #define ZYD_CR34 ZYD_REG_CTL(0x0088) #define ZYD_CR35 ZYD_REG_CTL(0x008C) #define ZYD_CR36 ZYD_REG_CTL(0x0090) #define ZYD_CR37 ZYD_REG_CTL(0x0094) #define ZYD_CR38 ZYD_REG_CTL(0x0098) #define ZYD_CR39 ZYD_REG_CTL(0x009C) #define ZYD_CR40 ZYD_REG_CTL(0x00A0) #define ZYD_CR41 ZYD_REG_CTL(0x00A4) #define ZYD_CR42 ZYD_REG_CTL(0x00A8) #define ZYD_CR43 ZYD_REG_CTL(0x00AC) #define ZYD_CR44 ZYD_REG_CTL(0x00B0) #define ZYD_CR45 ZYD_REG_CTL(0x00B4) #define ZYD_CR46 ZYD_REG_CTL(0x00B8) #define ZYD_CR47 ZYD_REG_CTL(0x00BC) /* CCK baseband gain (patch value might be in EEPROM) */ #define ZYD_CR48 ZYD_REG_CTL(0x00C0) #define ZYD_CR49 ZYD_REG_CTL(0x00C4) #define ZYD_CR50 ZYD_REG_CTL(0x00C8) #define ZYD_CR51 ZYD_REG_CTL(0x00CC) /* TX power control for RF in 6-36M modes */ #define ZYD_CR52 ZYD_REG_CTL(0x00D0) /* TX power control for RF in 48M mode */ #define ZYD_CR53 ZYD_REG_CTL(0x00D4) /* TX power control for RF in 54M mode */ #define ZYD_CR54 ZYD_REG_CTL(0x00D8) #define ZYD_CR55 ZYD_REG_CTL(0x00DC) #define ZYD_CR56 ZYD_REG_CTL(0x00E0) #define ZYD_CR57 ZYD_REG_CTL(0x00E4) #define ZYD_CR58 ZYD_REG_CTL(0x00E8) #define ZYD_CR59 ZYD_REG_CTL(0x00EC) #define ZYD_CR60 ZYD_REG_CTL(0x00F0) #define ZYD_CR61 ZYD_REG_CTL(0x00F4) #define ZYD_CR62 ZYD_REG_CTL(0x00F8) #define ZYD_CR63 ZYD_REG_CTL(0x00FC) #define ZYD_CR64 ZYD_REG_CTL(0x0100) #define ZYD_CR65 ZYD_REG_CTL(0x0104) /* OFDM 54M calibration */ #define ZYD_CR66 ZYD_REG_CTL(0x0108) /* OFDM 48M calibration */ #define ZYD_CR67 ZYD_REG_CTL(0x010C) /* OFDM 36M calibration */ #define ZYD_CR68 ZYD_REG_CTL(0x0110) /* CCK calibration */ #define ZYD_CR69 ZYD_REG_CTL(0x0114) #define ZYD_CR70 ZYD_REG_CTL(0x0118) #define ZYD_CR71 ZYD_REG_CTL(0x011C) #define ZYD_CR72 ZYD_REG_CTL(0x0120) #define ZYD_CR73 ZYD_REG_CTL(0x0124) #define ZYD_CR74 ZYD_REG_CTL(0x0128) #define ZYD_CR75 ZYD_REG_CTL(0x012C) #define ZYD_CR76 ZYD_REG_CTL(0x0130) #define ZYD_CR77 ZYD_REG_CTL(0x0134) #define ZYD_CR78 ZYD_REG_CTL(0x0138) #define ZYD_CR79 ZYD_REG_CTL(0x013C) #define ZYD_CR80 ZYD_REG_CTL(0x0140) #define ZYD_CR81 ZYD_REG_CTL(0x0144) #define ZYD_CR82 ZYD_REG_CTL(0x0148) #define ZYD_CR83 ZYD_REG_CTL(0x014C) #define ZYD_CR84 ZYD_REG_CTL(0x0150) #define ZYD_CR85 ZYD_REG_CTL(0x0154) #define ZYD_CR86 ZYD_REG_CTL(0x0158) #define ZYD_CR87 ZYD_REG_CTL(0x015C) #define ZYD_CR88 ZYD_REG_CTL(0x0160) #define ZYD_CR89 ZYD_REG_CTL(0x0164) #define ZYD_CR90 ZYD_REG_CTL(0x0168) #define ZYD_CR91 ZYD_REG_CTL(0x016C) #define ZYD_CR92 ZYD_REG_CTL(0x0170) #define ZYD_CR93 ZYD_REG_CTL(0x0174) #define ZYD_CR94 ZYD_REG_CTL(0x0178) #define ZYD_CR95 ZYD_REG_CTL(0x017C) #define ZYD_CR96 ZYD_REG_CTL(0x0180) #define ZYD_CR97 ZYD_REG_CTL(0x0184) #define ZYD_CR98 ZYD_REG_CTL(0x0188) #define ZYD_CR99 ZYD_REG_CTL(0x018C) #define ZYD_CR100 ZYD_REG_CTL(0x0190) #define ZYD_CR101 ZYD_REG_CTL(0x0194) #define ZYD_CR102 ZYD_REG_CTL(0x0198) #define ZYD_CR103 ZYD_REG_CTL(0x019C) #define ZYD_CR104 ZYD_REG_CTL(0x01A0) #define ZYD_CR105 ZYD_REG_CTL(0x01A4) #define ZYD_CR106 ZYD_REG_CTL(0x01A8) #define ZYD_CR107 ZYD_REG_CTL(0x01AC) #define ZYD_CR108 ZYD_REG_CTL(0x01B0) #define ZYD_CR109 ZYD_REG_CTL(0x01B4) #define ZYD_CR110 ZYD_REG_CTL(0x01B8) #define ZYD_CR111 ZYD_REG_CTL(0x01BC) #define ZYD_CR112 ZYD_REG_CTL(0x01C0) #define ZYD_CR113 ZYD_REG_CTL(0x01C4) #define ZYD_CR114 ZYD_REG_CTL(0x01C8) #define ZYD_CR115 ZYD_REG_CTL(0x01CC) #define ZYD_CR116 ZYD_REG_CTL(0x01D0) #define ZYD_CR117 ZYD_REG_CTL(0x01D4) #define ZYD_CR118 ZYD_REG_CTL(0x01D8) #define ZYD_CR119 ZYD_REG_CTL(0x01DC) #define ZYD_CR120 ZYD_REG_CTL(0x01E0) #define ZYD_CR121 ZYD_REG_CTL(0x01E4) #define ZYD_CR122 ZYD_REG_CTL(0x01E8) #define ZYD_CR123 ZYD_REG_CTL(0x01EC) #define ZYD_CR124 ZYD_REG_CTL(0x01F0) #define ZYD_CR125 ZYD_REG_CTL(0x01F4) #define ZYD_CR126 ZYD_REG_CTL(0x01F8) #define ZYD_CR127 ZYD_REG_CTL(0x01FC) #define ZYD_CR128 ZYD_REG_CTL(0x0200) #define ZYD_CR129 ZYD_REG_CTL(0x0204) #define ZYD_CR130 ZYD_REG_CTL(0x0208) #define ZYD_CR131 ZYD_REG_CTL(0x020C) #define ZYD_CR132 ZYD_REG_CTL(0x0210) #define ZYD_CR133 ZYD_REG_CTL(0x0214) #define ZYD_CR134 ZYD_REG_CTL(0x0218) #define ZYD_CR135 ZYD_REG_CTL(0x021C) #define ZYD_CR136 ZYD_REG_CTL(0x0220) #define ZYD_CR137 ZYD_REG_CTL(0x0224) #define ZYD_CR138 ZYD_REG_CTL(0x0228) #define ZYD_CR139 ZYD_REG_CTL(0x022C) #define ZYD_CR140 ZYD_REG_CTL(0x0230) #define ZYD_CR141 ZYD_REG_CTL(0x0234) #define ZYD_CR142 ZYD_REG_CTL(0x0238) #define ZYD_CR143 ZYD_REG_CTL(0x023C) #define ZYD_CR144 ZYD_REG_CTL(0x0240) #define ZYD_CR145 ZYD_REG_CTL(0x0244) #define ZYD_CR146 ZYD_REG_CTL(0x0248) #define ZYD_CR147 ZYD_REG_CTL(0x024C) #define ZYD_CR148 ZYD_REG_CTL(0x0250) #define ZYD_CR149 ZYD_REG_CTL(0x0254) #define ZYD_CR150 ZYD_REG_CTL(0x0258) #define ZYD_CR151 ZYD_REG_CTL(0x025C) #define ZYD_CR152 ZYD_REG_CTL(0x0260) #define ZYD_CR153 ZYD_REG_CTL(0x0264) #define ZYD_CR154 ZYD_REG_CTL(0x0268) #define ZYD_CR155 ZYD_REG_CTL(0x026C) #define ZYD_CR156 ZYD_REG_CTL(0x0270) #define ZYD_CR157 ZYD_REG_CTL(0x0274) #define ZYD_CR158 ZYD_REG_CTL(0x0278) #define ZYD_CR159 ZYD_REG_CTL(0x027C) #define ZYD_CR160 ZYD_REG_CTL(0x0280) #define ZYD_CR161 ZYD_REG_CTL(0x0284) #define ZYD_CR162 ZYD_REG_CTL(0x0288) #define ZYD_CR163 ZYD_REG_CTL(0x028C) #define ZYD_CR164 ZYD_REG_CTL(0x0290) #define ZYD_CR165 ZYD_REG_CTL(0x0294) #define ZYD_CR166 ZYD_REG_CTL(0x0298) #define ZYD_CR167 ZYD_REG_CTL(0x029C) #define ZYD_CR168 ZYD_REG_CTL(0x02A0) #define ZYD_CR169 ZYD_REG_CTL(0x02A4) #define ZYD_CR170 ZYD_REG_CTL(0x02A8) #define ZYD_CR171 ZYD_REG_CTL(0x02AC) #define ZYD_CR172 ZYD_REG_CTL(0x02B0) #define ZYD_CR173 ZYD_REG_CTL(0x02B4) #define ZYD_CR174 ZYD_REG_CTL(0x02B8) #define ZYD_CR175 ZYD_REG_CTL(0x02BC) #define ZYD_CR176 ZYD_REG_CTL(0x02C0) #define ZYD_CR177 ZYD_REG_CTL(0x02C4) #define ZYD_CR178 ZYD_REG_CTL(0x02C8) #define ZYD_CR179 ZYD_REG_CTL(0x02CC) #define ZYD_CR180 ZYD_REG_CTL(0x02D0) #define ZYD_CR181 ZYD_REG_CTL(0x02D4) #define ZYD_CR182 ZYD_REG_CTL(0x02D8) #define ZYD_CR183 ZYD_REG_CTL(0x02DC) #define ZYD_CR184 ZYD_REG_CTL(0x02E0) #define ZYD_CR185 ZYD_REG_CTL(0x02E4) #define ZYD_CR186 ZYD_REG_CTL(0x02E8) #define ZYD_CR187 ZYD_REG_CTL(0x02EC) #define ZYD_CR188 ZYD_REG_CTL(0x02F0) #define ZYD_CR189 ZYD_REG_CTL(0x02F4) #define ZYD_CR190 ZYD_REG_CTL(0x02F8) #define ZYD_CR191 ZYD_REG_CTL(0x02FC) #define ZYD_CR192 ZYD_REG_CTL(0x0300) #define ZYD_CR193 ZYD_REG_CTL(0x0304) #define ZYD_CR194 ZYD_REG_CTL(0x0308) #define ZYD_CR195 ZYD_REG_CTL(0x030C) #define ZYD_CR196 ZYD_REG_CTL(0x0310) #define ZYD_CR197 ZYD_REG_CTL(0x0314) #define ZYD_CR198 ZYD_REG_CTL(0x0318) #define ZYD_CR199 ZYD_REG_CTL(0x031C) #define ZYD_CR200 ZYD_REG_CTL(0x0320) #define ZYD_CR201 ZYD_REG_CTL(0x0324) #define ZYD_CR202 ZYD_REG_CTL(0x0328) #define ZYD_CR203 ZYD_REG_CTL(0x032C) /* I2C bus template value & flash control */ #define ZYD_CR204 ZYD_REG_CTL(0x0330) #define ZYD_CR205 ZYD_REG_CTL(0x0334) #define ZYD_CR206 ZYD_REG_CTL(0x0338) #define ZYD_CR207 ZYD_REG_CTL(0x033C) #define ZYD_CR208 ZYD_REG_CTL(0x0340) #define ZYD_CR209 ZYD_REG_CTL(0x0344) #define ZYD_CR210 ZYD_REG_CTL(0x0348) #define ZYD_CR211 ZYD_REG_CTL(0x034C) #define ZYD_CR212 ZYD_REG_CTL(0x0350) #define ZYD_CR213 ZYD_REG_CTL(0x0354) #define ZYD_CR214 ZYD_REG_CTL(0x0358) #define ZYD_CR215 ZYD_REG_CTL(0x035C) #define ZYD_CR216 ZYD_REG_CTL(0x0360) #define ZYD_CR217 ZYD_REG_CTL(0x0364) #define ZYD_CR218 ZYD_REG_CTL(0x0368) #define ZYD_CR219 ZYD_REG_CTL(0x036C) #define ZYD_CR220 ZYD_REG_CTL(0x0370) #define ZYD_CR221 ZYD_REG_CTL(0x0374) #define ZYD_CR222 ZYD_REG_CTL(0x0378) #define ZYD_CR223 ZYD_REG_CTL(0x037C) #define ZYD_CR224 ZYD_REG_CTL(0x0380) #define ZYD_CR225 ZYD_REG_CTL(0x0384) #define ZYD_CR226 ZYD_REG_CTL(0x0388) #define ZYD_CR227 ZYD_REG_CTL(0x038C) #define ZYD_CR228 ZYD_REG_CTL(0x0390) #define ZYD_CR229 ZYD_REG_CTL(0x0394) #define ZYD_CR230 ZYD_REG_CTL(0x0398) #define ZYD_CR231 ZYD_REG_CTL(0x039C) #define ZYD_CR232 ZYD_REG_CTL(0x03A0) #define ZYD_CR233 ZYD_REG_CTL(0x03A4) #define ZYD_CR234 ZYD_REG_CTL(0x03A8) #define ZYD_CR235 ZYD_REG_CTL(0x03AC) #define ZYD_CR236 ZYD_REG_CTL(0x03B0) #define ZYD_CR240 ZYD_REG_CTL(0x03C0) /* bit 7: host-controlled RF register writes * ZYD_CR241-ZYD_CR245: for hardware controlled writing of RF bits, not needed for * USB */ #define ZYD_CR241 ZYD_REG_CTL(0x03C4) #define ZYD_CR242 ZYD_REG_CTL(0x03C8) #define ZYD_CR243 ZYD_REG_CTL(0x03CC) #define ZYD_CR244 ZYD_REG_CTL(0x03D0) #define ZYD_CR245 ZYD_REG_CTL(0x03D4) #define ZYD_CR251 ZYD_REG_CTL(0x03EC) /* only used for activation and deactivation of * Airoha RFs AL2230 and AL7230B */ #define ZYD_CR252 ZYD_REG_CTL(0x03F0) #define ZYD_CR253 ZYD_REG_CTL(0x03F4) #define ZYD_CR254 ZYD_REG_CTL(0x03F8) #define ZYD_CR255 ZYD_REG_CTL(0x03FC) /* Copied nearly verbatim from the Linux driver rewrite */ #define ZYD_DEF_CR \ { ZYD_CR0, 0x0a }, { ZYD_CR1, 0x06 }, { ZYD_CR2, 0x26 }, \ { ZYD_CR3, 0x38 }, { ZYD_CR4, 0x80 }, { ZYD_CR9, 0xa0 }, \ { ZYD_CR10, 0x81 }, { ZYD_CR11, 0x00 }, { ZYD_CR12, 0x7f }, \ { ZYD_CR13, 0x8c }, { ZYD_CR14, 0x80 }, { ZYD_CR15, 0x3d }, \ { ZYD_CR16, 0x20 }, { ZYD_CR17, 0x1e }, { ZYD_CR18, 0x0a }, \ { ZYD_CR19, 0x48 }, { ZYD_CR20, 0x0c }, { ZYD_CR21, 0x0c }, \ { ZYD_CR22, 0x23 }, { ZYD_CR23, 0x90 }, { ZYD_CR24, 0x14 }, \ { ZYD_CR25, 0x40 }, { ZYD_CR26, 0x10 }, { ZYD_CR27, 0x19 }, \ { ZYD_CR28, 0x7f }, { ZYD_CR29, 0x80 }, { ZYD_CR30, 0x4b }, \ { ZYD_CR31, 0x60 }, { ZYD_CR32, 0x43 }, { ZYD_CR33, 0x08 }, \ { ZYD_CR34, 0x06 }, { ZYD_CR35, 0x0a }, { ZYD_CR36, 0x00 }, \ { ZYD_CR37, 0x00 }, { ZYD_CR38, 0x38 }, { ZYD_CR39, 0x0c }, \ { ZYD_CR40, 0x84 }, { ZYD_CR41, 0x2a }, { ZYD_CR42, 0x80 }, \ { ZYD_CR43, 0x10 }, { ZYD_CR44, 0x12 }, { ZYD_CR46, 0xff }, \ { ZYD_CR47, 0x08 }, { ZYD_CR48, 0x26 }, { ZYD_CR49, 0x5b }, \ { ZYD_CR64, 0xd0 }, { ZYD_CR65, 0x04 }, { ZYD_CR66, 0x58 }, \ { ZYD_CR67, 0xc9 }, { ZYD_CR68, 0x88 }, { ZYD_CR69, 0x41 }, \ { ZYD_CR70, 0x23 }, { ZYD_CR71, 0x10 }, { ZYD_CR72, 0xff }, \ { ZYD_CR73, 0x32 }, { ZYD_CR74, 0x30 }, { ZYD_CR75, 0x65 }, \ { ZYD_CR76, 0x41 }, { ZYD_CR77, 0x1b }, { ZYD_CR78, 0x30 }, \ { ZYD_CR79, 0x68 }, { ZYD_CR80, 0x64 }, { ZYD_CR81, 0x64 }, \ { ZYD_CR82, 0x00 }, { ZYD_CR83, 0x00 }, { ZYD_CR84, 0x00 }, \ { ZYD_CR85, 0x02 }, { ZYD_CR86, 0x00 }, { ZYD_CR87, 0x00 }, \ { ZYD_CR88, 0xff }, { ZYD_CR89, 0xfc }, { ZYD_CR90, 0x00 }, \ { ZYD_CR91, 0x00 }, { ZYD_CR92, 0x00 }, { ZYD_CR93, 0x08 }, \ { ZYD_CR94, 0x00 }, { ZYD_CR95, 0x00 }, { ZYD_CR96, 0xff }, \ { ZYD_CR97, 0xe7 }, { ZYD_CR98, 0x00 }, { ZYD_CR99, 0x00 }, \ { ZYD_CR100, 0x00 }, { ZYD_CR101, 0xae }, { ZYD_CR102, 0x02 }, \ { ZYD_CR103, 0x00 }, { ZYD_CR104, 0x03 }, { ZYD_CR105, 0x65 }, \ { ZYD_CR106, 0x04 }, { ZYD_CR107, 0x00 }, { ZYD_CR108, 0x0a }, \ { ZYD_CR109, 0xaa }, { ZYD_CR110, 0xaa }, { ZYD_CR111, 0x25 }, \ { ZYD_CR112, 0x25 }, { ZYD_CR113, 0x00 }, { ZYD_CR119, 0x1e }, \ { ZYD_CR125, 0x90 }, { ZYD_CR126, 0x00 }, { ZYD_CR127, 0x00 }, \ { ZYD_CR5, 0x00 }, { ZYD_CR6, 0x00 }, { ZYD_CR7, 0x00 }, \ { ZYD_CR8, 0x00 }, { ZYD_CR9, 0x20 }, { ZYD_CR12, 0xf0 }, \ { ZYD_CR20, 0x0e }, { ZYD_CR21, 0x0e }, { ZYD_CR27, 0x10 }, \ { ZYD_CR44, 0x33 }, { ZYD_CR47, 0x30 }, { ZYD_CR83, 0x24 }, \ { ZYD_CR84, 0x04 }, { ZYD_CR85, 0x00 }, { ZYD_CR86, 0x0C }, \ { ZYD_CR87, 0x12 }, { ZYD_CR88, 0x0C }, { ZYD_CR89, 0x00 }, \ { ZYD_CR90, 0x10 }, { ZYD_CR91, 0x08 }, { ZYD_CR93, 0x00 }, \ { ZYD_CR94, 0x01 }, { ZYD_CR95, 0x00 }, { ZYD_CR96, 0x50 }, \ { ZYD_CR97, 0x37 }, { ZYD_CR98, 0x35 }, { ZYD_CR101, 0x13 }, \ { ZYD_CR102, 0x27 }, { ZYD_CR103, 0x27 }, { ZYD_CR104, 0x18 }, \ { ZYD_CR105, 0x12 }, { ZYD_CR109, 0x27 }, { ZYD_CR110, 0x27 }, \ { ZYD_CR111, 0x27 }, { ZYD_CR112, 0x27 }, { ZYD_CR113, 0x27 }, \ { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x26 }, { ZYD_CR116, 0x24 }, \ { ZYD_CR117, 0xfc }, { ZYD_CR118, 0xfa }, { ZYD_CR120, 0x4f }, \ { ZYD_CR123, 0x27 }, { ZYD_CR125, 0xaa }, { ZYD_CR127, 0x03 }, \ { ZYD_CR128, 0x14 }, { ZYD_CR129, 0x12 }, { ZYD_CR130, 0x10 }, \ { ZYD_CR131, 0x0C }, { ZYD_CR136, 0xdf }, { ZYD_CR137, 0x40 }, \ { ZYD_CR138, 0xa0 }, { ZYD_CR139, 0xb0 }, { ZYD_CR140, 0x99 }, \ { ZYD_CR141, 0x82 }, { ZYD_CR142, 0x54 }, { ZYD_CR143, 0x1c }, \ { ZYD_CR144, 0x6c }, { ZYD_CR147, 0x07 }, { ZYD_CR148, 0x4c }, \ { ZYD_CR149, 0x50 }, { ZYD_CR150, 0x0e }, { ZYD_CR151, 0x18 }, \ { ZYD_CR160, 0xfe }, { ZYD_CR161, 0xee }, { ZYD_CR162, 0xaa }, \ { ZYD_CR163, 0xfa }, { ZYD_CR164, 0xfa }, { ZYD_CR165, 0xea }, \ { ZYD_CR166, 0xbe }, { ZYD_CR167, 0xbe }, { ZYD_CR168, 0x6a }, \ { ZYD_CR169, 0xba }, { ZYD_CR170, 0xba }, { ZYD_CR171, 0xba }, \ /* Note: ZYD_CR204 must lead the ZYD_CR203 */ \ { ZYD_CR204, 0x7d }, { ZYD_CR203, 0x30 }/*, { ZYD_CR240, 0x80 }*/ #define ZYD_DEF_MAC \ { ZYD_MAC_ACK_EXT, 0x20 }, \ { ZYD_CR_ADDA_MBIAS_WARMTIME, 0x30000808 }, \ { ZYD_MAC_RETRY, 0x2 }, \ { ZYD_MAC_SNIFFER, 0 }, \ { ZYD_MAC_STOHOSTSETTING, 0 }, \ { ZYD_MAC_GHTBL, 0x00 }, \ { ZYD_MAC_GHTBH, 0x80000000 }, \ { ZYD_MAC_MISC, 0xa4 }, \ { ZYD_CR_ADDA_PWR_DWN, 0x7f }, \ { ZYD_MAC_BCNCFG, 0x00f00401 }, \ { ZYD_MAC_PHY_DELAY2, 0x00 }, \ { ZYD_MAC_ACK_EXT, 0x80 }, \ { ZYD_CR_ADDA_PWR_DWN, 0x00 }, \ { ZYD_MAC_SIFS_ACK_TIME, 0x100 }, \ { ZYD_MAC_DIFS_EIFS_SIFS, 0x547c032 }, \ { ZYD_CR_RX_PE_DELAY, 0x70 }, \ { ZYD_CR_PS_CTRL, 0x10000000 }, \ { ZYD_MAC_RTSCTSRATE, 0x02030203 }, \ { ZYD_MAC_RX_THRESHOLD, 0x000c0640 }, \ { ZYD_MAC_AFTER_PNP, 0x1 }, \ { ZYD_MAC_BACKOFF_PROTECT, 0x114 } /* Copied nearly verbatim from the Linux driver rewrite */ #define ZYD_RFMD_CR \ { ZYD_CR2, 0x1E }, { ZYD_CR9, 0x20 }, { ZYD_CR10, 0x89 }, \ { ZYD_CR11, 0x00 }, { ZYD_CR15, 0xD0 }, { ZYD_CR17, 0x68 }, \ { ZYD_CR19, 0x4a }, { ZYD_CR20, 0x0c }, { ZYD_CR21, 0x0E }, \ { ZYD_CR23, 0x48 }, \ /* normal size for cca threshold */ \ { ZYD_CR24, 0x14 }, \ /* { ZYD_CR24, 0x20 }, */ \ { ZYD_CR26, 0x90 }, { ZYD_CR27, 0x30 }, { ZYD_CR29, 0x20 }, \ { ZYD_CR31, 0xb2 }, { ZYD_CR32, 0x43 }, { ZYD_CR33, 0x28 }, \ { ZYD_CR38, 0x30 }, { ZYD_CR34, 0x0f }, { ZYD_CR35, 0xF0 }, \ { ZYD_CR41, 0x2a }, { ZYD_CR46, 0x7F }, { ZYD_CR47, 0x1e }, \ { ZYD_CR51, 0xc5 }, { ZYD_CR52, 0xc5 }, { ZYD_CR53, 0xc5 }, \ { ZYD_CR79, 0x58 }, { ZYD_CR80, 0x30 }, { ZYD_CR81, 0x30 }, \ { ZYD_CR82, 0x00 }, { ZYD_CR83, 0x24 }, { ZYD_CR84, 0x04 }, \ { ZYD_CR85, 0x00 }, { ZYD_CR86, 0x10 }, { ZYD_CR87, 0x2A }, \ { ZYD_CR88, 0x10 }, { ZYD_CR89, 0x24 }, { ZYD_CR90, 0x18 }, \ /* { ZYD_CR91, 0x18 }, */ \ /* should solve continous CTS frame problems */ \ { ZYD_CR91, 0x00 }, \ { ZYD_CR92, 0x0a }, { ZYD_CR93, 0x00 }, { ZYD_CR94, 0x01 }, \ { ZYD_CR95, 0x00 }, { ZYD_CR96, 0x40 }, { ZYD_CR97, 0x37 }, \ { ZYD_CR98, 0x05 }, { ZYD_CR99, 0x28 }, { ZYD_CR100, 0x00 }, \ { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, { ZYD_CR103, 0x27 }, \ { ZYD_CR104, 0x18 }, { ZYD_CR105, 0x12 }, \ /* normal size */ \ { ZYD_CR106, 0x1a }, \ /* { ZYD_CR106, 0x22 }, */ \ { ZYD_CR107, 0x24 }, { ZYD_CR108, 0x0a }, { ZYD_CR109, 0x13 }, \ { ZYD_CR110, 0x2F }, { ZYD_CR111, 0x27 }, { ZYD_CR112, 0x27 }, \ { ZYD_CR113, 0x27 }, { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x40 }, \ { ZYD_CR116, 0x40 }, { ZYD_CR117, 0xF0 }, { ZYD_CR118, 0xF0 }, \ { ZYD_CR119, 0x16 }, \ /* no TX continuation */ \ { ZYD_CR122, 0x00 }, \ /* { ZYD_CR122, 0xff }, */ \ { ZYD_CR127, 0x03 }, { ZYD_CR131, 0x08 }, { ZYD_CR138, 0x28 }, \ { ZYD_CR148, 0x44 }, { ZYD_CR150, 0x10 }, { ZYD_CR169, 0xBB }, \ { ZYD_CR170, 0xBB } #define ZYD_RFMD_RF \ 0x000007, /* REG0(CFG1) */ \ 0x07dd43, /* REG1(IFPLL1) */ \ 0x080959, /* REG2(IFPLL2) */ \ 0x0e6666, \ 0x116a57, /* REG4 */ \ 0x17dd43, /* REG5 */ \ 0x1819f9, /* REG6 */ \ 0x1e6666, \ 0x214554, \ 0x25e7fa, \ 0x27fffa, \ /* The Zydas driver somehow forgets to set this value. It's * only set for Japan. We are using internal power control * for now. */ \ 0x294128, /* internal power */ \ /* 0x28252c, */ /* External control TX power */ \ /* CR31_CCK, CR51_6-36M, CR52_48M, CR53_54M */ \ 0x2c0000, \ 0x300000, \ 0x340000, /* REG13(0xD) */ \ 0x381e0f, /* REG14(0xE) */ \ /* Bogus, RF2959's data sheet doesn't know register 27, which is * actually referenced here. */ \ 0x6c180f /* REG27(0x11) */ #define ZYD_RFMD_CHANTABLE \ { 0x181979, 0x1e6666 }, \ { 0x181989, 0x1e6666 }, \ { 0x181999, 0x1e6666 }, \ { 0x1819a9, 0x1e6666 }, \ { 0x1819b9, 0x1e6666 }, \ { 0x1819c9, 0x1e6666 }, \ { 0x1819d9, 0x1e6666 }, \ { 0x1819e9, 0x1e6666 }, \ { 0x1819f9, 0x1e6666 }, \ { 0x181a09, 0x1e6666 }, \ { 0x181a19, 0x1e6666 }, \ { 0x181a29, 0x1e6666 }, \ { 0x181a39, 0x1e6666 }, \ { 0x181a60, 0x1c0000 } #define ZYD_RFMD_RADIO_ON \ { ZYD_CR10, 0x89 }, \ { ZYD_CR11, 0x00 } #define ZYD_RFMD_RADIO_OFF \ { ZYD_CR10, 0x15 }, \ { ZYD_CR11, 0x81 } #define ZYD_AL2230_CR \ { ZYD_CR15, 0x20 }, { ZYD_CR23, 0x40 }, { ZYD_CR24, 0x20 }, \ { ZYD_CR26, 0x11 }, { ZYD_CR28, 0x3e }, { ZYD_CR29, 0x00 }, \ { ZYD_CR44, 0x33 }, { ZYD_CR106, 0x2a }, { ZYD_CR107, 0x1a }, \ { ZYD_CR109, 0x09 }, { ZYD_CR110, 0x27 }, { ZYD_CR111, 0x2b }, \ { ZYD_CR112, 0x2b }, { ZYD_CR119, 0x0a }, { ZYD_CR10, 0x89 }, \ /* for newest (3rd cut) AL2300 */ \ { ZYD_CR17, 0x28 }, \ { ZYD_CR26, 0x93 }, { ZYD_CR34, 0x30 }, \ /* for newest (3rd cut) AL2300 */ \ { ZYD_CR35, 0x3e }, \ { ZYD_CR41, 0x24 }, { ZYD_CR44, 0x32 }, \ /* for newest (3rd cut) AL2300 */ \ { ZYD_CR46, 0x96 }, \ { ZYD_CR47, 0x1e }, { ZYD_CR79, 0x58 }, { ZYD_CR80, 0x30 }, \ { ZYD_CR81, 0x30 }, { ZYD_CR87, 0x0a }, { ZYD_CR89, 0x04 }, \ { ZYD_CR92, 0x0a }, { ZYD_CR99, 0x28 }, { ZYD_CR100, 0x00 }, \ { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, { ZYD_CR106, 0x24 }, \ { ZYD_CR107, 0x2a }, { ZYD_CR109, 0x09 }, { ZYD_CR110, 0x13 }, \ { ZYD_CR111, 0x1f }, { ZYD_CR112, 0x1f }, { ZYD_CR113, 0x27 }, \ { ZYD_CR114, 0x27 }, \ /* for newest (3rd cut) AL2300 */ \ { ZYD_CR115, 0x24 }, \ { ZYD_CR116, 0x24 }, { ZYD_CR117, 0xf4 }, { ZYD_CR118, 0xfc }, \ { ZYD_CR119, 0x10 }, { ZYD_CR120, 0x4f }, { ZYD_CR121, 0x77 }, \ { ZYD_CR122, 0xe0 }, { ZYD_CR137, 0x88 }, { ZYD_CR252, 0xff }, \ { ZYD_CR253, 0xff }, \ /* These following happen separately in vendor drv */ \ { }, \ /* shdnb(PLL_ON)=0 */ \ { ZYD_CR251, 0x2f }, \ /* shdnb(PLL_ON)=1 */ \ { ZYD_CR251, 0x3f }, \ { ZYD_CR138, 0x28 }, { ZYD_CR203, 0x06 } #define ZYD_AL2230_RF \ /* Channel 1 */ \ 0x03f790, \ 0x033331, \ 0x00000d, \ \ 0x0b3331, \ 0x03b812, \ 0x00fff3, \ 0x000da4, \ 0x0f4dc5, /* fix freq shift, 0x04edc5 */ \ 0x0805b6, \ 0x011687, \ 0x000688, \ 0x0403b9, /* external control TX power (CR31) */ \ 0x00dbba, \ 0x00099b, \ 0x0bdffc, \ 0x00000d, \ 0x00500f, \ \ /* These writes happen separately in the vendor driver */ \ 0x00d00f, \ 0x004c0f, \ 0x00540f, \ 0x00700f, \ 0x00500f #define ZYD_AL2230_CHANTABLE \ { 0x03f790, 0x033331, 0x00000d }, \ { 0x03f790, 0x0b3331, 0x00000d }, \ { 0x03e790, 0x033331, 0x00000d }, \ { 0x03e790, 0x0b3331, 0x00000d }, \ { 0x03f7a0, 0x033331, 0x00000d }, \ { 0x03f7a0, 0x0b3331, 0x00000d }, \ { 0x03e7a0, 0x033331, 0x00000d }, \ { 0x03e7a0, 0x0b3331, 0x00000d }, \ { 0x03f7b0, 0x033331, 0x00000d }, \ { 0x03f7b0, 0x0b3331, 0x00000d }, \ { 0x03e7b0, 0x033331, 0x00000d }, \ { 0x03e7b0, 0x0b3331, 0x00000d }, \ { 0x03f7c0, 0x033331, 0x00000d }, \ { 0x03e7c0, 0x066661, 0x00000d } #define ZYD_AL2230_SETCHANNEL \ { ZYD_CR138, 0x28 }, \ { ZYD_CR203, 0x06 } #define ZYD_AL2230_RADIO_ON \ { ZYD_CR11, 0x00 }, \ { ZYD_CR251, 0x3f } #define ZYD_AL2230_RADIO_OFF \ { ZYD_CR11, 0x04 }, \ { ZYD_CR251, 0x2f } #define ZYD_TR_BULK_DT_WR 0 #define ZYD_TR_BULK_DT_RD 1 #define ZYD_TR_BULK_CS_WR 2 #define ZYD_TR_BULK_CS_RD 3 #define ZYD_TR_INTR_DT_WR 4 #define ZYD_TR_INTR_DT_RD 5 #define ZYD_TR_INTR_CS_WR 6 #define ZYD_TR_INTR_CS_RD 7 #define ZYD_TR_MAX 8 /* * Device configurations and types */ static const char *zyd_rfs[] = { "unknown", "unknown", "UW2451", "UCHIP", "AL2230", "AL7230B", "THETA", "AL2210", "MAXIM_NEW", "GCT", "PV2000", "RALINK", "INTERSIL", "RFMD", "MAXIM_NEW2", "PHILIPS" }; struct zyd_channel_range { int regdomain; uint8_t start; uint8_t end; /* exclusive (channel must be less than end) */ }; /* * Blows size up (132 bytes, but not that smaller than extra * mapping code, so ... */ static const struct zyd_channel_range zyd_channel_ranges[] = { { 0, 0, 0 }, { ZYD_REGDOMAIN_FCC, 1, 12 }, { ZYD_REGDOMAIN_IC, 1, 12 }, { ZYD_REGDOMAIN_ETSI, 1, 14 }, { ZYD_REGDOMAIN_JAPAN, 1, 14 }, { ZYD_REGDOMAIN_SPAIN, 10, 12 }, { ZYD_REGDOMAIN_FRANCE, 10, 14 }, { ZYD_REGDOMAIN_JAPAN_ADD, 14, 15 }, { -1, 0, 0 } }; /* * Supported rates for 802.11b/g modes (in 500Kbps unit). */ static const struct ieee80211_rateset zyd_rateset_11b = { 4, { 2, 4, 11, 22 } }; static const struct ieee80211_rateset zyd_rateset_11g = { 8, { 12, 18, 24, 36, 48, 72, 96, 108 } }; /* * Various structure content enums */ /* * Driver internal */ enum zyd_operationmode { OM_NONEYET = 0, OM_ADHOC = 1, OM_INFRASTRUCTURE = 2 }; enum zyd_cmd { ZC_NONE, ZC_SCAN, ZC_JOIN }; enum zyd_encrtypes { ENC_NOWEP = 0, ENC_WEP64 = 1, ENC_WEP128 = 5, ENC_WEP256 = 6, ENC_SNIFFER = 8 }; enum zyd_macflags { ZMF_FIXED_CHANNEL = 1 }; enum zyd_stohostflags { STH_ASS_REQ = 1, STH_ASS_RSP = 2, STH_REASS_REQ = 4, STH_REASS_RSP = 8, STH_PRB_REQ = 16, STH_PRB_RSP = 32, STH_BCN = 256, STH_ATIM = 512, STH_DEASS = 1024, STH_AUTH = 2048, STH_DEAUTH = 4096, STH_PS_POLL = 67108864UL, STH_RTS = 134217728UL, STH_CTS = 268435456UL, STH_ACK = 536870912UL, STH_CFE = 1073741824UL, STH_CFE_A = 2147483648UL }; /* * Control set format */ enum zyd_controlsetformatrate { /* MBPS rates for Direct Sequence Spread Spectrum */ CSF_RT_CCK_1 = 0x00, CSF_RT_CCK_2 = 0x01, CSF_RT_CCK_5_5 = 0x02, CSF_RT_CCK_11 = 0x03, /* MBPS rates for Orthogonal Frequency Division Multiplexing */ CSF_RT_OFDM_6 = 0x0b, CSF_RT_OFDM_9 = 0x0f, CSF_RT_OFDM_12 = 0x0a, CSF_RT_OFDM_18 = 0x0e, CSF_RT_OFDM_24 = 0x09, CSF_RT_OFDM_36 = 0x0d, CSF_RT_OFDM_48 = 0x08, CSF_RT_OFDM_54 = 0x0c }; enum zyd_controlsetformatmodtype { CSF_MT_CCK = 0, CSF_MT_OFDM = 1 }; enum zyd_controlsetformatpreamblecck { CSF_PM_CCK_LONG = 0, CSF_PM_CCK_SHORT = 1 }; enum zyd_controlsetformatpreambleofdm { CSF_PM_OFDM_11G = 0, CSF_PM_OFDM_11A = 1 }; enum zyd_controlsetformatbackoff { CSF_BO_SIFS = 0, /* SIFS (fragmentation) */ CSF_BO_RAND = 1 /* Need random backoff */ }; enum zyd_controlsetformatmulticast { CSF_MC_UNICAST = 0, CSF_MC_MULTICAST = 1 }; enum zyd_controlsetformatframetype { CSF_FT_DATAFRAME = 0, /* Date frame (header size = 24) */ CSF_FT_POLLFRAME = 1, /* Poll frame (needn't modify duration ID */ CSF_FT_MGMTFRAME = 2, /* Management frame (header size = 24) */ CSF_FT_NOSEQCONTROL = 3 /* No sequence control (header size = 16) */ }; enum zyd_controlsetformatwakedst { CSF_WD_DONTWAKEUP = 0, CSF_WD_WAKEUP = 1 }; enum zyd_controlsetformatrts { CSF_RTS_NORTSFRAME = 0, CSF_RTS_NEEDRTSFRAME = 1 }; enum zyd_controlsetformatencryption { CSF_ENC_NOENCRYPTION = 0, CSF_ENC_NEEDENCRYPTION = 1 }; enum zyd_controlsetformatselfcts { CSF_SC_NOTSCFRAME = 0, CSF_SC_SCFRAME = 1 }; /* * Rx status report */ enum zyd_rxstatusreportdecrtype { RSR_DT_NOWEP = 0, RSR_DT_WEP64 = 1, RSR_DT_TKIP = 2, /* 3 is reserved */ RSR_DT_AES = 4, RSR_DT_WEP128 = 5, RSR_DT_WEP256 = 6 }; enum zyd_rxstatusreportframestatmodtype { RSR_FS_MT_CCK = 0, RSR_FS_MT_OFDM = 1 }; /* These are flags */ enum zyd_rxstatusreportframestaterrreason { RSR_FS_ER_TIMEOUT = 0x02, RSR_FS_ER_FIFO_OVERRUN = 0x04, RSR_FS_ER_DECRYPTION_ERROR = 0x08, RSR_FS_ER_CRC32_ERROR = 0x10, RSR_FS_ER_ADDR1_NOT_MATCH = 0x20, RSR_FS_ER_CRC16_ERROR = 0x40 }; enum zyd_rxstatusreportframestaterrind { RSR_FS_EI_ERROR_INDICATION = 0x80 }; /* * Structures */ struct zyd_controlsetformat { uint8_t rate :4; uint8_t modulationtype :1; uint8_t preamble :1; uint8_t reserved :2; uint16_t txlen; uint8_t needbackoff :1; uint8_t multicast :1; uint8_t frametype :2; uint8_t wakedst :1; uint8_t rts :1; uint8_t encryption :1; uint8_t selfcts :1; uint16_t packetlength; uint16_t currentlength; uint8_t service; uint16_t nextframelen; } UPACKED; struct zyd_rxstatusreport { uint8_t signalstrength; uint8_t signalqualitycck; uint8_t signalqualityofdm; uint8_t decryptiontype; uint8_t modulationtype :1; uint8_t rxerrorreason :6; uint8_t errorindication :1; } UPACKED; /* Appended length info for multiframe transmission */ struct zyd_rxleninfoapp { uWord len[3]; uWord marker; /* 0x697E */ #define ZYD_MULTIFRAME_MARKER 0x697E } UPACKED; struct zyd_aw_pt_bi { uint32_t atim_wnd_period; uint32_t pre_tbtt; uint32_t beacon_interval; }; /* RF control. Kinda driver-in-driver via function pointers. */ struct zyd_softc; /* FORWARD */ struct zyd_rf { uint8_t flags; uint8_t type; void (*cfg_init_hw)(struct zyd_softc *, struct zyd_rf *); void (*cfg_switch_radio)(struct zyd_softc *, uint8_t onoff); void (*cfg_set_channel)(struct zyd_softc *, struct zyd_rf *, uint8_t); }; #define ZYD_REQ_RFWRITE_BITS_MAX 29 struct zyd_req_rfwrite { uWord id; uWord value; /* 1: 3683a */ /* 2: other (default) */ uWord bits; /* RF2595: 24 */ uWord bit_values[ZYD_REQ_RFWRITE_BITS_MAX]; /* (CR203 & ~(RF_IF_LE | RF_CLK | RF_DATA)) | (bit ? RF_DATA : 0) */ } UPACKED; struct zyd_macaddr { uint8_t addr[6]; } UPACKED; /* Control interface @ EP 0 */ struct zyd_control { uint8_t type; uint8_t id; uint16_t value; uint16_t index; uint16_t length; void* data; } UPACKED; /* Int IN interface @ EP 3 (single register access) */ struct zyd_intinsingle { uWord id; uWord sts1; uWord sts2; uWord sts3; uWord sts4; uWord sts5; } UPACKED; /* Int/bulk OUT interface @ EP 4 (single register access) */ struct zyd_intoutsingle { uWord id; uWord par1; uWord par2; uWord length; uWord data1; uWord data2; } UPACKED; /* Register addr/data combo */ struct zyd_regadcombo { uWord addr; uWord data; } UPACKED; /* Int IN interface @ EP 3 (multi register access) output */ struct zyd_intinmultioutput { uWord id; /* pairs of addr-data-addr-data-... */ struct zyd_regadcombo registers[15]; } UPACKED; /* Int/bulk OUT interface @ EP 4 (multi register access) read */ struct zyd_intoutmultiread { uWord id; uWord addr[15]; } UPACKED; /* Int/bulk OUT interface @ EP 4 (multi register access) write */ struct zyd_intoutmultiwrite { uWord id; /* pairs of addr-data-addr-data-... */ struct zyd_regadcombo registers[15]; } UPACKED; /* Pairs of address and 16-bit data. For batch write. */ struct zyd_adpairs16 { uint32_t addr; uint16_t data; }; /* Pairs of address and 32-bit data. For batch write. */ struct zyd_adpairs32 { uint32_t addr; uint32_t data; }; /* * Sending packets of more than 1500 bytes confuses some access points, so the * default MTU is set to 1500 but can be increased up to 2310 bytes using * ifconfig */ #define ZYD_DEFAULT_MTU 1500 #define ZYD_MAX_MTU (2312 - 2) #define ZYD_DEFAULT_CHANNEL 10 /* quickly determine if a given rate is CCK or OFDM */ #define ZYD_RATE_IS_OFDM(rate) (((rate) >= 12) && ((rate) != 22)) #define ZYD_ACK_SIZE 14 /* 10 + 4(FCS) */ #define ZYD_CTS_SIZE 14 /* 10 + 4(FCS) */ #define ZYD_SIFS 10 /* PLCP header at beginning of any frame */ #define ZYD_PLCP_HDR_SIZE 5 struct zyd_rx_radiotap_header { struct ieee80211_radiotap_header wr_ihdr; uint8_t wr_flags; uint16_t wr_chan_freq; uint16_t wr_chan_flags; uint8_t wr_rssi; uint8_t wr_max_rssi; } __packed; #define ZYD_RX_RADIOTAP_PRESENT \ ((1 << IEEE80211_RADIOTAP_FLAGS) | \ (1 << IEEE80211_RADIOTAP_CHANNEL) | \ (1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL)) struct zyd_tx_radiotap_header { struct ieee80211_radiotap_header wt_ihdr; uint8_t wt_flags; uint8_t wt_rate; uint16_t wt_chan_freq; uint16_t wt_chan_flags; uint8_t wt_rssi; uint8_t wt_max_rssi; } __packed; #define ZYD_TX_RADIOTAP_PRESENT \ ((1 << IEEE80211_RADIOTAP_FLAGS) | \ (1 << IEEE80211_RADIOTAP_RATE) | \ (1 << IEEE80211_RADIOTAP_CHANNEL)) struct zyd_softc { struct ieee80211com sc_ic; struct zyd_rf sc_rf; struct __callout sc_scan_callout; struct __callout sc_watchdog; struct mtx sc_mtx; struct usbd_config_td sc_config_td; struct ieee80211_beacon_offsets sc_bo; union { struct zyd_rx_radiotap_header th; uint8_t pad[64]; } sc_rxtapu; union { struct zyd_tx_radiotap_header th; uint8_t pad[64]; } sc_txtapu; int (*sc_newstate)(struct ieee80211com *, enum ieee80211_state, int); struct bpf_if *sc_drvbpf; struct usbd_xfer *sc_xfer[ZYD_TR_MAX]; struct usbd_device *sc_udev; struct ifnet *sc_ifp; enum zyd_operationmode sc_operation; enum zyd_encrtypes sc_encrypt; uint32_t sc_rxtap_len; uint32_t sc_txtap_len; uint32_t sc_unit; uint16_t sc_firmware_base; uint16_t sc_fw_ver; uint8_t sc_intr_obuf[ZYD_INTR_BUF_SIZE]; uint8_t sc_intr_ibuf[ZYD_INTR_BUF_SIZE]; uint8_t sc_intr_iwakeup; uint8_t sc_intr_owakeup; uint8_t sc_intr_olen; uint8_t sc_default_regdomain; uint8_t sc_regdomain; uint8_t sc_channel; uint8_t sc_mac_flags; uint8_t sc_pa_ver; uint8_t sc_pwr_cal_values[ZYD_E2P_CHANNEL_COUNT]; uint8_t sc_pwr_int_values[ZYD_E2P_CHANNEL_COUNT]; uint8_t sc_ofdm_cal_values[3][ZYD_E2P_CHANNEL_COUNT]; uint8_t sc_flags; #define ZYD_FLAG_INTR_READ_STALL 0x01 #define ZYD_FLAG_INTR_WRITE_STALL 0x02 #define ZYD_FLAG_BULK_READ_STALL 0x04 #define ZYD_FLAG_BULK_WRITE_STALL 0x08 #define ZYD_FLAG_TX_BEACON 0x10 #define ZYD_FLAG_HL_READY 0x20 #define ZYD_FLAG_LL_READY 0x40 #define ZYD_FLAG_WAIT_COMMAND 0x80 uint8_t sc_name[16]; }; struct zyd_config_copy { struct { uint32_t chan_to_ieee; uint8_t chan_is_2ghz; } ic_curchan; struct { struct { uint8_t chan_is_5ghz; } ni_chan; uint16_t ni_intval; uint8_t ni_bssid[IEEE80211_ADDR_LEN]; } ic_bss; struct { struct { ieee80211_keyix wk_keyix; uint8_t wk_key[IEEE80211_KEYBUF_SIZE]; } cs_nw_keys[IEEE80211_WEP_NKID]; } ic_crypto; enum ieee80211_opmode ic_opmode; enum ieee80211_state ic_state; uint32_t ic_flags; uint32_t if_flags; uint16_t ic_txpowlimit; uint16_t ic_curmode; uint8_t ic_myaddr[IEEE80211_ADDR_LEN]; }; pwcbsd/usb/ohci.c000644 000423 000000 00000176701 10551670753 014502 0ustar00luigiwheel000000 000000 /* $NetBSD: ohci.c,v 1.138 2003/02/08 03:32:50 ichiro Exp $ */ /* Also, already ported: * $NetBSD: ohci.c,v 1.140 2003/05/13 04:42:00 gson Exp $ * $NetBSD: ohci.c,v 1.141 2003/09/10 20:08:29 mycroft Exp $ * $NetBSD: ohci.c,v 1.142 2003/10/11 03:04:26 toshii Exp $ * $NetBSD: ohci.c,v 1.143 2003/10/18 04:50:35 simonb Exp $ * $NetBSD: ohci.c,v 1.144 2003/11/23 19:18:06 augustss Exp $ * $NetBSD: ohci.c,v 1.145 2003/11/23 19:20:25 augustss Exp $ * $NetBSD: ohci.c,v 1.146 2003/12/29 08:17:10 toshii Exp $ * $NetBSD: ohci.c,v 1.147 2004/06/22 07:20:35 mycroft Exp $ * $NetBSD: ohci.c,v 1.148 2004/06/22 18:27:46 mycroft Exp $ */ #include __FBSDID("$FreeBSD: src/sys/dev/usb/ohci.c,v 1.164 2006/09/07 00:06:41 imp Exp $"); /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * USB Open Host Controller driver. * * OHCI spec: http://www.compaq.com/productinfo/development/openhci.html * USB spec: http://www.usb.org/developers/docs/usbspec.zip */ #include #include #include #include #include /* LIST_XXX() */ #include #include #define INCLUDE_PCIXXX_H #include #include #include #include #define MS_TO_TICKS(ms) (((ms) * hz) / 1000) #define OHCI_BUS2SC(bus) ((ohci_softc_t *)(((u_int8_t *)(bus)) - \ POINTER_TO_UNSIGNED(&(((ohci_softc_t *)0)->sc_bus)))) #ifdef USB_DEBUG #undef DPRINTF #undef DPRINTFN #define DPRINTF(x) { if (ohcidebug) { printf("%s: ", __FUNCTION__); printf x ; } } #define DPRINTFN(n,x) { if (ohcidebug > (n)) { printf("%s: ", __FUNCTION__); printf x ; } } int ohcidebug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, ohci, CTLFLAG_RW, 0, "USB ohci"); SYSCTL_INT(_hw_usb_ohci, OID_AUTO, debug, CTLFLAG_RW, &ohcidebug, 0, "ohci debug level"); static void ohci_dumpregs(ohci_softc_t *); static void ohci_dump_tds(ohci_td_t *); static void ohci_dump_td(ohci_td_t *); static void ohci_dump_ed(ohci_ed_t *); static void ohci_dump_itd(ohci_itd_t *); static void ohci_dump_itds(ohci_itd_t *); #endif #define OBARR(sc) bus_space_barrier((sc)->iot, (sc)->ioh, 0, (sc)->sc_size, \ BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE) #define OWRITE1(sc, r, x) \ do { OBARR(sc); bus_space_write_1((sc)->iot, (sc)->ioh, (r), (x)); } while (0) #define OWRITE2(sc, r, x) \ do { OBARR(sc); bus_space_write_2((sc)->iot, (sc)->ioh, (r), (x)); } while (0) #define OWRITE4(sc, r, x) \ do { OBARR(sc); bus_space_write_4((sc)->iot, (sc)->ioh, (r), (x)); } while (0) #define OREAD1(sc, r) (OBARR(sc), bus_space_read_1((sc)->iot, (sc)->ioh, (r))) #define OREAD2(sc, r) (OBARR(sc), bus_space_read_2((sc)->iot, (sc)->ioh, (r))) #define OREAD4(sc, r) (OBARR(sc), bus_space_read_4((sc)->iot, (sc)->ioh, (r))) #define OHCI_INTR_ENDPT 1 extern struct usbd_bus_methods ohci_bus_methods; extern struct usbd_pipe_methods ohci_device_bulk_methods; extern struct usbd_pipe_methods ohci_device_ctrl_methods; extern struct usbd_pipe_methods ohci_device_intr_methods; extern struct usbd_pipe_methods ohci_device_isoc_methods; extern struct usbd_pipe_methods ohci_root_ctrl_methods; extern struct usbd_pipe_methods ohci_root_intr_methods; #define PHYSADDR(sc,what) \ ((sc)->sc_physaddr + POINTER_TO_UNSIGNED(&(((struct ohci_softc *)0)->what))) static usbd_status ohci_controller_init(ohci_softc_t *sc) { int i; u_int32_t s, ctl, ival, hcr, fm, per, desca; /* Determine in what context we are running. */ ctl = OREAD4(sc, OHCI_CONTROL); if(ctl & OHCI_IR) { /* SMM active, request change */ DPRINTF(("SMM active, request owner change\n")); s = OREAD4(sc, OHCI_COMMAND_STATUS); OWRITE4(sc, OHCI_COMMAND_STATUS, s | OHCI_OCR); for(i = 0; (i < 100) && (ctl & OHCI_IR); i++) { DELAY(1000*1); ctl = OREAD4(sc, OHCI_CONTROL); } if((ctl & OHCI_IR) == 0) { device_printf(sc->sc_bus.bdev, "SMM does not respond, resetting\n"); OWRITE4(sc, OHCI_CONTROL, OHCI_HCFS_RESET); goto reset; } #if 0 /* Don't bother trying to reuse the BIOS init, we'll reset it anyway. */ } else if((ctl & OHCI_HCFS_MASK) != OHCI_HCFS_RESET) { /* BIOS started controller. */ DPRINTF(("BIOS active\n")); if((ctl & OHCI_HCFS_MASK) != OHCI_HCFS_OPERATIONAL) { OWRITE4(sc, OHCI_CONTROL, OHCI_HCFS_OPERATIONAL); DELAY(1000*USB_RESUME_DELAY); } #endif } else { DPRINTF(("cold started\n")); reset: /* controller was cold started */ DELAY(1000*USB_BUS_RESET_DELAY); } /* * This reset should not be necessary according to the OHCI spec, but * without it some controllers do not start. */ DPRINTF(("%s: resetting\n", device_get_nameunit(sc->sc_bus.bdev))); OWRITE4(sc, OHCI_CONTROL, OHCI_HCFS_RESET); DELAY(1000*USB_BUS_RESET_DELAY); /* we now own the host controller and the bus has been reset */ ival = OHCI_GET_IVAL(OREAD4(sc, OHCI_FM_INTERVAL)); OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_HCR); /* Reset HC */ /* nominal time for a reset is 10 us */ for(i = 0; i < 10; i++) { DELAY(10); hcr = OREAD4(sc, OHCI_COMMAND_STATUS) & OHCI_HCR; if(!hcr) { break; } } if (hcr) { device_printf(sc->sc_bus.bdev, "reset timeout\n"); return (USBD_IOERROR); } #ifdef USB_DEBUG if(ohcidebug > 15) { ohci_dumpregs(sc); } #endif /* The controller is now in SUSPEND state, we have 2ms to finish. */ /* set up HC registers */ OWRITE4(sc, OHCI_HCCA, PHYSADDR(sc, sc_hw.hcca)); OWRITE4(sc, OHCI_CONTROL_HEAD_ED, PHYSADDR(sc, sc_hw.ctrl_start)); OWRITE4(sc, OHCI_BULK_HEAD_ED, PHYSADDR(sc, sc_hw.bulk_start)); /* disable all interrupts and then switch on all desired interrupts */ OWRITE4(sc, OHCI_INTERRUPT_DISABLE, OHCI_ALL_INTRS); OWRITE4(sc, OHCI_INTERRUPT_ENABLE, sc->sc_eintrs | OHCI_MIE); /* switch on desired functional features */ ctl = OREAD4(sc, OHCI_CONTROL); ctl &= ~(OHCI_CBSR_MASK | OHCI_LES | OHCI_HCFS_MASK | OHCI_IR); ctl |= OHCI_PLE | OHCI_IE | OHCI_CLE | OHCI_BLE | OHCI_RATIO_1_4 | OHCI_HCFS_OPERATIONAL; /* And finally start it! */ OWRITE4(sc, OHCI_CONTROL, ctl); /* * The controller is now OPERATIONAL. Set a some final * registers that should be set earlier, but that the * controller ignores when in the SUSPEND state. */ fm = (OREAD4(sc, OHCI_FM_INTERVAL) & OHCI_FIT) ^ OHCI_FIT; fm |= OHCI_FSMPS(ival) | ival; OWRITE4(sc, OHCI_FM_INTERVAL, fm); per = OHCI_PERIODIC(ival); /* 90% periodic */ OWRITE4(sc, OHCI_PERIODIC_START, per); /* Fiddle the No OverCurrent Protection bit to avoid chip bug. */ desca = OREAD4(sc, OHCI_RH_DESCRIPTOR_A); OWRITE4(sc, OHCI_RH_DESCRIPTOR_A, desca | OHCI_NOCP); OWRITE4(sc, OHCI_RH_STATUS, OHCI_LPSC); /* Enable port power */ DELAY(1000*OHCI_ENABLE_POWER_DELAY); OWRITE4(sc, OHCI_RH_DESCRIPTOR_A, desca); /* * The AMD756 requires a delay before re-reading the register, * otherwise it will occasionally report 0 ports. */ sc->sc_noport = 0; for(i = 0; (i < 10) && (sc->sc_noport == 0); i++) { DELAY(1000*OHCI_READ_DESC_DELAY); sc->sc_noport = OHCI_GET_NDP(OREAD4(sc, OHCI_RH_DESCRIPTOR_A)); } #ifdef USB_DEBUG if(ohcidebug > 5) { ohci_dumpregs(sc); } #endif return (USBD_NORMAL_COMPLETION); } usbd_status ohci_init(ohci_softc_t *sc) { u_int i; u_int16_t bit; u_int16_t x; u_int16_t y; mtx_lock(&sc->sc_bus.mtx); DPRINTF(("start\n")); sc->sc_eintrs = OHCI_NORMAL_INTRS; /* * setup self pointers */ sc->sc_hw.ctrl_start.ed_self = htole32(PHYSADDR(sc,sc_hw.ctrl_start)); sc->sc_hw.ctrl_start.ed_flags = htole32(OHCI_ED_SKIP); sc->sc_ctrl_p_last = &sc->sc_hw.ctrl_start; sc->sc_hw.bulk_start.ed_self = htole32(PHYSADDR(sc,sc_hw.bulk_start)); sc->sc_hw.bulk_start.ed_flags = htole32(OHCI_ED_SKIP); sc->sc_bulk_p_last = &sc->sc_hw.bulk_start; sc->sc_hw.isoc_start.ed_self = htole32(PHYSADDR(sc,sc_hw.isoc_start)); sc->sc_hw.isoc_start.ed_flags = htole32(OHCI_ED_SKIP); sc->sc_isoc_p_last = &sc->sc_hw.isoc_start; for(i = 0; i < OHCI_NO_EDS; i++) { sc->sc_hw.intr_start[i].ed_self = htole32(PHYSADDR(sc,sc_hw.intr_start[i])); sc->sc_hw.intr_start[i].ed_flags = htole32(OHCI_ED_SKIP); sc->sc_intr_p_last[i] = &sc->sc_hw.intr_start[i]; } /* * the QHs are arranged to give poll intervals that are * powers of 2 times 1ms */ bit = OHCI_NO_EDS/2; while(bit) { x = bit; while(x & bit) { y = (x ^ bit)|(bit/2); /* the next QH has half the * poll interval */ sc->sc_hw.intr_start[x].next = NULL; sc->sc_hw.intr_start[x].ed_next = sc->sc_hw.intr_start[y].ed_self; x++; } bit >>= 1; } /* the last (1ms) QH */ sc->sc_hw.intr_start[0].next = &sc->sc_hw.isoc_start; sc->sc_hw.intr_start[0].ed_next = sc->sc_hw.isoc_start.ed_self; /* * Fill HCCA interrupt table. The bit reversal is to get * the tree set up properly to spread the interrupts. */ for(i = 0; i < OHCI_NO_INTRS; i++) { sc->sc_hw.hcca.hcca_interrupt_table[i] = sc->sc_hw.intr_start[i|(OHCI_NO_EDS/2)].ed_self; } LIST_INIT(&sc->sc_interrupt_list_head); /* set up the bus struct */ sc->sc_bus.methods = &ohci_bus_methods; __callout_init_mtx(&sc->sc_tmo_rhsc, &sc->sc_bus.mtx, CALLOUT_RETURNUNLOCKED); #ifdef USB_DEBUG if(ohcidebug > 15) { for(i = 0; i < OHCI_NO_EDS; i++) { printf("ed#%d ", i); ohci_dump_ed(&sc->sc_hw.intr_start[i]); } printf("iso "); ohci_dump_ed(&sc->sc_hw.isoc_start); } #endif sc->sc_control = sc->sc_intre = 0; device_printf(sc->sc_bus.bdev, " "); sc->sc_bus.usbrev = USBREV_1_0; if(ohci_controller_init(sc)) { mtx_unlock(&sc->sc_bus.mtx); return (USBD_INVAL); } else { mtx_unlock(&sc->sc_bus.mtx); return (USBD_NORMAL_COMPLETION); } } /* * shut down the controller when the system is going down */ void ohci_detach(struct ohci_softc *sc) { mtx_lock(&sc->sc_bus.mtx); __callout_stop(&sc->sc_tmo_rhsc); OWRITE4(sc, OHCI_INTERRUPT_DISABLE, OHCI_ALL_INTRS); OWRITE4(sc, OHCI_CONTROL, OHCI_HCFS_RESET); DELAY(1000*300); /* XXX let stray task complete */ mtx_unlock(&sc->sc_bus.mtx); __callout_drain(&(sc->sc_tmo_rhsc)); return; } /* NOTE: suspend/resume is called from * interrupt context and cannot sleep! */ void ohci_suspend(ohci_softc_t *sc) { u_int32_t ctl; mtx_lock(&sc->sc_bus.mtx); #ifdef USB_DEBUG DPRINTF(("\n")); if(ohcidebug > 2) { ohci_dumpregs(sc); } #endif ctl = OREAD4(sc, OHCI_CONTROL) & ~OHCI_HCFS_MASK; if(sc->sc_control == 0) { /* * Preserve register values, in case that APM BIOS * does not recover them. */ sc->sc_control = ctl; sc->sc_intre = OREAD4(sc, OHCI_INTERRUPT_ENABLE); } ctl |= OHCI_HCFS_SUSPEND; OWRITE4(sc, OHCI_CONTROL, ctl); DELAY(1000*USB_RESUME_WAIT); mtx_unlock(&sc->sc_bus.mtx); return; } void ohci_resume(ohci_softc_t *sc) { u_int32_t ctl; mtx_lock(&sc->sc_bus.mtx); #ifdef USB_DEBUG DPRINTF(("\n")); if(ohcidebug > 2) { ohci_dumpregs(sc); } #endif /* some broken BIOSes never initialize the Controller chip */ ohci_controller_init(sc); if(sc->sc_intre) { OWRITE4(sc, OHCI_INTERRUPT_ENABLE, sc->sc_intre & (OHCI_ALL_INTRS | OHCI_MIE)); } if(sc->sc_control) ctl = sc->sc_control; else ctl = OREAD4(sc, OHCI_CONTROL); ctl |= OHCI_HCFS_RESUME; OWRITE4(sc, OHCI_CONTROL, ctl); DELAY(1000*USB_RESUME_DELAY); ctl = (ctl & ~OHCI_HCFS_MASK) | OHCI_HCFS_OPERATIONAL; OWRITE4(sc, OHCI_CONTROL, ctl); DELAY(1000*USB_RESUME_RECOVERY); sc->sc_control = sc->sc_intre = 0; mtx_unlock(&sc->sc_bus.mtx); return; } #ifdef USB_DEBUG static void ohci_dumpregs(ohci_softc_t *sc) { DPRINTF(("ohci_dumpregs: rev=0x%08x control=0x%08x command=0x%08x\n", OREAD4(sc, OHCI_REVISION), OREAD4(sc, OHCI_CONTROL), OREAD4(sc, OHCI_COMMAND_STATUS))); DPRINTF((" intrstat=0x%08x intre=0x%08x intrd=0x%08x\n", OREAD4(sc, OHCI_INTERRUPT_STATUS), OREAD4(sc, OHCI_INTERRUPT_ENABLE), OREAD4(sc, OHCI_INTERRUPT_DISABLE))); DPRINTF((" hcca=0x%08x percur=0x%08x ctrlhd=0x%08x\n", OREAD4(sc, OHCI_HCCA), OREAD4(sc, OHCI_PERIOD_CURRENT_ED), OREAD4(sc, OHCI_CONTROL_HEAD_ED))); DPRINTF((" ctrlcur=0x%08x bulkhd=0x%08x bulkcur=0x%08x\n", OREAD4(sc, OHCI_CONTROL_CURRENT_ED), OREAD4(sc, OHCI_BULK_HEAD_ED), OREAD4(sc, OHCI_BULK_CURRENT_ED))); DPRINTF((" done=0x%08x fmival=0x%08x fmrem=0x%08x\n", OREAD4(sc, OHCI_DONE_HEAD), OREAD4(sc, OHCI_FM_INTERVAL), OREAD4(sc, OHCI_FM_REMAINING))); DPRINTF((" fmnum=0x%08x perst=0x%08x lsthrs=0x%08x\n", OREAD4(sc, OHCI_FM_NUMBER), OREAD4(sc, OHCI_PERIODIC_START), OREAD4(sc, OHCI_LS_THRESHOLD))); DPRINTF((" desca=0x%08x descb=0x%08x stat=0x%08x\n", OREAD4(sc, OHCI_RH_DESCRIPTOR_A), OREAD4(sc, OHCI_RH_DESCRIPTOR_B), OREAD4(sc, OHCI_RH_STATUS))); DPRINTF((" port1=0x%08x port2=0x%08x\n", OREAD4(sc, OHCI_RH_PORT_STATUS(1)), OREAD4(sc, OHCI_RH_PORT_STATUS(2)))); DPRINTF((" HCCA: frame_number=0x%04x done_head=0x%08x\n", le32toh(sc->sc_hw.hcca.hcca_frame_number), le32toh(sc->sc_hw.hcca.hcca_done_head))); return; } static void ohci_dump_tds(ohci_td_t *std) { for(; std; std = std->obj_next) { ohci_dump_td(std); if (std->td_next == 0) { break; } } return; } static void ohci_dump_td(ohci_td_t *std) { u_int32_t td_flags = le32toh(std->td_flags); printf("TD(%p) at %08lx: %s%s%s%s%s delay=%d ec=%d " "cc=%d\ncbp=0x%08lx next=0x%08lx be=0x%08lx\n", std, (long)le32toh(std->td_self), (td_flags & OHCI_TD_R) ? "-R" : "", (td_flags & OHCI_TD_OUT) ? "-OUT" : "", (td_flags & OHCI_TD_IN) ? "-IN" : "", ((td_flags & OHCI_TD_TOGGLE_MASK) == OHCI_TD_TOGGLE_1) ? "-TOG1" : "", ((td_flags & OHCI_TD_TOGGLE_MASK) == OHCI_TD_TOGGLE_0) ? "-TOG0" : "", OHCI_TD_GET_DI(le32toh(std->td_flags)), OHCI_TD_GET_EC(le32toh(std->td_flags)), OHCI_TD_GET_CC(le32toh(std->td_flags)), (long)le32toh(std->td_cbp), (long)le32toh(std->td_next), (long)le32toh(std->td_be)); return; } static void ohci_dump_itd(ohci_itd_t *sitd) { int i; printf("ITD(%p) at %08lx: sf=%d di=%d fc=%d cc=%d\n" "bp0=0x%08lx next=0x%08lx be=0x%08lx\n", sitd, (long)le32toh(sitd->itd_self), OHCI_ITD_GET_SF(le32toh(sitd->itd_flags)), OHCI_ITD_GET_DI(le32toh(sitd->itd_flags)), OHCI_ITD_GET_FC(le32toh(sitd->itd_flags)), OHCI_ITD_GET_CC(le32toh(sitd->itd_flags)), (long)le32toh(sitd->itd_bp0), (long)le32toh(sitd->itd_next), (long)le32toh(sitd->itd_be)); for(i = 0; i < OHCI_ITD_NOFFSET; i++) { printf("offs[%d]=0x%04x ", i, (u_int)le16toh(sitd->itd_offset[i])); } printf("\n"); return; } static void ohci_dump_itds(ohci_itd_t *sitd) { for(; sitd; sitd = sitd->obj_next) { ohci_dump_itd(sitd); if (sitd->itd_next == 0) { break; } } return; } static void ohci_dump_ed(ohci_ed_t *sed) { u_int32_t ed_flags = le32toh(sed->ed_flags); u_int32_t ed_headp = le32toh(sed->ed_headp); printf("ED(%p) at 0x%08lx: addr=%d endpt=%d maxp=%d flags=%s%s%s%s%s\n" "tailp=0x%08lx headflags=%s%s headp=0x%08lx nexted=0x%08lx\n", sed, (long)le32toh(sed->ed_self), OHCI_ED_GET_FA(le32toh(sed->ed_flags)), OHCI_ED_GET_EN(le32toh(sed->ed_flags)), OHCI_ED_GET_MAXP(le32toh(sed->ed_flags)), (ed_flags & OHCI_ED_DIR_OUT) ? "-OUT" : "", (ed_flags & OHCI_ED_DIR_IN) ? "-IN" : "", (ed_flags & OHCI_ED_SPEED) ? "-LOWSPEED" : "", (ed_flags & OHCI_ED_SKIP) ? "-SKIP" : "", (ed_flags & OHCI_ED_FORMAT_ISO) ? "-ISO" : "", (long)le32toh(sed->ed_tailp), (ed_headp & OHCI_HALTED) ? "-HALTED" : "", (ed_headp & OHCI_TOGGLECARRY) ? "-CARRY" : "", (long)le32toh(sed->ed_headp), (long)le32toh(sed->ed_next)); return; } #endif #define OHCI_APPEND_QH(sed,last) (last) = _ohci_append_qh(sed,last) static ohci_ed_t * _ohci_append_qh(ohci_ed_t *sed, ohci_ed_t *last) { DPRINTFN(10, ("%p to %p\n", sed, last)); /* (sc->sc_bus.mtx) must be locked */ sed->next = last->next; sed->ed_next = last->ed_next; sed->prev = last; /* the last->next->prev is never followed: * sed->next->prev = sed; */ last->next = sed; last->ed_next = sed->ed_self; return(sed); } #define OHCI_REMOVE_QH(sed,last) (last) = _ohci_remove_qh(sed,last) static ohci_ed_t * _ohci_remove_qh(ohci_ed_t *sed, ohci_ed_t *last) { DPRINTFN(10, ("%p from %p\n", sed, last)); /* (sc->sc_bus.mtx) must be locked */ /* only remove if not removed from a queue */ if(sed->prev) { sed->prev->next = sed->next; sed->prev->ed_next = sed->ed_next; if(sed->next) { sed->next->prev = sed->prev; } /* terminate transfer in case the * transferred packet was short so * that the ED still points at the * last used TD */ sed->ed_flags |= htole32(OHCI_ED_SKIP); sed->ed_headp = sed->ed_tailp; last = ((last == sed) ? sed->prev : last); sed->prev = 0; } return(last); } static void ohci_device_done(struct usbd_xfer *xfer, usbd_status error); static void ohci_isoc_done(struct usbd_xfer *xfer) { u_int8_t nframes; u_int32_t actlen = 0; u_int16_t *plen = xfer->frlengths; __volatile__ u_int16_t *olen; u_int16_t len = 0; ohci_itd_t *td = xfer->td_transfer_first; while(1) { if(td == NULL) { panic("%s:%d: out of TD's\n", __FUNCTION__, __LINE__); } #ifdef USB_DEBUG if(ohcidebug > 5) { DPRINTFN(-1,("isoc TD\n")); ohci_dump_itd(td); } #endif nframes = td->frames; olen = &td->itd_offset[0]; if (nframes > 8) { nframes = 8; } while(nframes--) { len = le16toh(*olen); if((len >> 12) == OHCI_CC_NOT_ACCESSED) { len = 0; } else { len &= ((1<<12)-1); } *plen = len; actlen += len; plen++; olen++; } if(((void *)td) == xfer->td_transfer_last) { break; } td = td->obj_next; } xfer->actlen = actlen; ohci_device_done(xfer,USBD_NORMAL_COMPLETION); return; } #ifdef USB_DEBUG static const char * const ohci_cc_strs[] = { "NO_ERROR", "CRC", "BIT_STUFFING", "DATA_TOGGLE_MISMATCH", "STALL", "DEVICE_NOT_RESPONDING", "PID_CHECK_FAILURE", "UNEXPECTED_PID", "DATA_OVERRUN", "DATA_UNDERRUN", "BUFFER_OVERRUN", "BUFFER_UNDERRUN", "reserved", "reserved", "NOT_ACCESSED", "NOT_ACCESSED" }; #endif static void ohci_non_isoc_done(struct usbd_xfer *xfer) { u_int16_t cc = 0; u_int32_t actlen = 0; u_int32_t len; u_int32_t temp; u_int32_t phy_start; u_int32_t phy_end; ohci_td_t *td = xfer->td_transfer_first; DPRINTFN(12, ("xfer=%p pipe=%p transfer done\n", xfer, xfer->pipe)); #ifdef USB_DEBUG if(ohcidebug > 10) { ohci_dump_tds(td); } #endif while(1) { if(td == NULL) { panic("%s:%d: out of TD's\n", __FUNCTION__, __LINE__); } len = td->len; if(td->td_cbp != 0) { /* compute the number of remaining * bytes in the hardware buffer: */ phy_start = le32toh(td->td_cbp); phy_end = le32toh(td->td_be); temp = (OHCI_PAGE(phy_start ^ phy_end) ? (OHCI_PAGE_SIZE+1) : 0x0001); temp += OHCI_PAGE_OFFSET(phy_end); temp -= OHCI_PAGE_OFFSET(phy_start); if (temp > len) { /* guard against corruption */ len = 0; } else { len -= temp; } } DPRINTFN(10, ("len=%d\n", len)); actlen += len; cc = OHCI_TD_GET_CC(le32toh(td->td_flags)); if(cc) { DPRINTFN(15,("error cc=%d (%s)\n", cc, ohci_cc_strs[cc])); break; } if(td->td_cbp != 0) { /* short transfer */ break; } if(((void *)td) == xfer->td_transfer_last) { break; } td = td->obj_next; } DPRINTFN(10, ("actlen=%d\n", actlen)); xfer->actlen = actlen; ohci_device_done(xfer, (cc == 0) ? USBD_NORMAL_COMPLETION : (cc == OHCI_CC_STALL) ? USBD_STALLED : USBD_IOERROR); return; } /* returns one when transfer is finished * and callback must be called; else zero */ static u_int8_t ohci_check_transfer(struct usbd_xfer *xfer, struct thread *ctd) { ohci_ed_t *ed = xfer->qh_start; DPRINTFN(15, ("xfer=%p\n", xfer)); if(xfer->usb_thread != ctd) { /* cannot call this transfer * back due to locking ! */ return 0; } if((ed->ed_flags & htole32(OHCI_ED_SKIP)) || (ed->ed_headp & htole32(OHCI_HALTED)) || (((ed->ed_headp ^ ed->ed_tailp) & htole32(-0x10)) == 0)) { if(xfer->pipe->methods == &ohci_device_isoc_methods) { /* isochronous transfer */ ohci_isoc_done(xfer); } else { /* store data-toggle */ if(ed->ed_headp & htole32(OHCI_TOGGLECARRY)) { xfer->pipe->toggle_next = 1; } else { xfer->pipe->toggle_next = 0; } /* non-isochronous transfer */ ohci_non_isoc_done(xfer); } return 1; } else { return 0; } } static void ohci_rhsc_enable(ohci_softc_t *sc) { struct usbd_callback_info info[1]; struct usbd_xfer *xfer; DPRINTFN(4, ("\n")); mtx_assert(&sc->sc_bus.mtx, MA_OWNED); sc->sc_eintrs |= OHCI_RHSC; OWRITE4(sc, OHCI_INTERRUPT_ENABLE, OHCI_RHSC); /* acknowledge any RHSC interrupt */ OWRITE4(sc, OHCI_INTERRUPT_STATUS, OHCI_RHSC); xfer = sc->sc_intrxfer; if(xfer) { /* transfer is transferred */ ohci_device_done(xfer, USBD_NORMAL_COMPLETION); /* queue callback */ info[0].xfer = xfer; info[0].refcount = xfer->usb_refcount; xfer->usb_root->memory_refcount++; mtx_unlock(&sc->sc_bus.mtx); usbd_do_callback(&info[0],&info[1]); } else { mtx_unlock(&sc->sc_bus.mtx); } return; } static void ohci_interrupt_td(ohci_softc_t *sc, struct thread *ctd) { enum { FINISH_LIST_MAX = 16 }; struct usbd_callback_info info[FINISH_LIST_MAX]; struct usbd_callback_info *ptr = &info[0]; struct usbd_xfer *xfer; u_int32_t status; u_int32_t done; u_int8_t need_repeat = 0; mtx_lock(&sc->sc_bus.mtx); if(sc->sc_bus.bdev == NULL) { /* too early interrupt */ goto done; } if(ctd) { /* the poll thread should not read * any status registers that will * clear interrupts! */ goto repeat; } sc->sc_bus.no_intrs++; DPRINTFN(15,("%s: real interrupt\n", device_get_nameunit(sc->sc_bus.bdev))); #ifdef USB_DEBUG if(ohcidebug > 15) { DPRINTF(("%s:\n", device_get_nameunit(sc->sc_bus.bdev))); ohci_dumpregs(sc); } #endif status = 0; done = le32toh(sc->sc_hw.hcca.hcca_done_head); /* The LSb of done is used to inform the HC Driver that an interrupt * condition exists for both the Done list and for another event * recorded in HcInterruptStatus. On an interrupt from the HC, the HC * Driver checks the HccaDoneHead Value. If this value is 0, then the * interrupt was caused by other than the HccaDoneHead update and the * HcInterruptStatus register needs to be accessed to determine that * exact interrupt cause. If HccaDoneHead is nonzero, then a Done list * update interrupt is indicated and if the LSb of done is nonzero, * then an additional interrupt event is indicated and * HcInterruptStatus should be checked to determine its cause. */ if(done != 0) { if(done & ~OHCI_DONE_INTRS) { status |= OHCI_WDH; } if(done & OHCI_DONE_INTRS) { status |= OREAD4(sc, OHCI_INTERRUPT_STATUS); done &= ~OHCI_DONE_INTRS; } sc->sc_hw.hcca.hcca_done_head = 0; } else { status = OREAD4(sc, OHCI_INTERRUPT_STATUS) & ~OHCI_WDH; } if(status == 0) /* nothing to be done (PCI shared interrupt) */ { goto done; } status &= ~OHCI_MIE; OWRITE4(sc, OHCI_INTERRUPT_STATUS, status); /* Acknowledge */ status &= sc->sc_eintrs; if(status == 0) { goto done; } #if 0 if(status & OHCI_SO) { /* XXX do what */ } #endif if(status & OHCI_RD) { device_printf(sc->sc_bus.bdev, "resume detect\n"); /* XXX process resume detect */ } if(status & OHCI_UE) { device_printf(sc->sc_bus.bdev, "unrecoverable error, " "controller halted\n"); OWRITE4(sc, OHCI_CONTROL, OHCI_HCFS_RESET); /* XXX what else */ } if(status & OHCI_RHSC) { xfer = sc->sc_intrxfer; if(xfer) { ohci_device_done(xfer, USBD_NORMAL_COMPLETION); /* queue callback */ ptr->xfer = xfer; ptr->refcount = xfer->usb_refcount; ptr++; xfer->usb_root->memory_refcount++; } /* * Disable RHSC interrupt for now, because it will be * on until the port has been reset. */ sc->sc_eintrs &= ~OHCI_RHSC; OWRITE4(sc, OHCI_INTERRUPT_DISABLE, OHCI_RHSC); /* do not allow RHSC interrupts > 1 per second */ __callout_reset(&sc->sc_tmo_rhsc, hz, (void *)(void *)ohci_rhsc_enable, sc); } status &= ~(OHCI_RHSC|OHCI_WDH|OHCI_SO); if(status != 0) { /* Block unprocessed interrupts. XXX */ OWRITE4(sc, OHCI_INTERRUPT_DISABLE, status); sc->sc_eintrs &= ~status; device_printf(sc->sc_bus.bdev, "blocking intrs 0x%x\n", status); } /* * when the host controller interrupts because a transfer * is completed, all active transfers are checked! */ repeat: LIST_FOREACH(xfer, &sc->sc_interrupt_list_head, interrupt_list) { /* check if transfer is * transferred */ if(ohci_check_transfer(xfer, ctd)) { /* queue callback */ ptr->xfer = xfer; ptr->refcount = xfer->usb_refcount; ptr++; xfer->usb_root->memory_refcount++; /* check queue length */ if(ptr >= &info[FINISH_LIST_MAX]) { need_repeat = 1; break; } } } done: mtx_unlock(&sc->sc_bus.mtx); usbd_do_callback(&info[0],ptr); if(need_repeat) { ptr = &info[0]; need_repeat = 0; mtx_lock(&sc->sc_bus.mtx); goto repeat; } return; } void ohci_interrupt(ohci_softc_t *sc) { ohci_interrupt_td(sc, NULL); return; } /* * called when a request does not complete */ static void ohci_timeout(struct usbd_xfer *xfer) { struct usbd_callback_info info[1]; ohci_softc_t *sc = xfer->usb_sc; DPRINTF(("xfer=%p\n", xfer)); mtx_assert(&sc->sc_bus.mtx, MA_OWNED); /* transfer is transferred */ ohci_device_done(xfer, USBD_TIMEOUT); /* queue callback */ info[0].xfer = xfer; info[0].refcount = xfer->usb_refcount; xfer->usb_root->memory_refcount++; mtx_unlock(&sc->sc_bus.mtx); usbd_do_callback(&info[0],&info[1]); return; } static void ohci_do_poll(struct usbd_bus *bus) { ohci_interrupt_td(OHCI_BUS2SC(bus), curthread); return; } #define ohci_add_interrupt_info(sc, xfer) \ LIST_INSERT_HEAD(&(sc)->sc_interrupt_list_head, (xfer), interrupt_list) static void ohci_remove_interrupt_info(struct usbd_xfer *xfer) { if((xfer)->interrupt_list.le_prev) { LIST_REMOVE((xfer), interrupt_list); (xfer)->interrupt_list.le_prev = NULL; } return; } static void ohci_setup_standard_chain(struct usbd_xfer *xfer, ohci_ed_t **ed_last) { struct usbd_page_search buf_res; /* the OHCI hardware can handle at most one 4k crossing per TD */ u_int32_t average = (OHCI_PAGE_SIZE - (OHCI_PAGE_SIZE % xfer->max_packet_size)); u_int32_t td_flags; u_int32_t buf_offset; u_int32_t len = xfer->length; u_int8_t isread; u_int8_t shortpkt = 0; ohci_td_t *td; ohci_td_t *td_last = NULL; ohci_ed_t *ed; DPRINTFN(8, ("addr=%d endpt=%d len=%d speed=%d\n", xfer->address, UE_GET_ADDR(xfer->endpoint), xfer->length, xfer->udev->speed)); td = (xfer->td_transfer_first = xfer->td_start); buf_offset = 0; usbd_get_page(&(xfer->buf_data), buf_offset, &buf_res); if(xfer->pipe->methods == &ohci_device_ctrl_methods) { /* the first byte is "bmRequestType" */ isread = *((u_int8_t *)(buf_res.buffer)); isread &= UT_READ; /* * check length ? */ xfer->pipe->toggle_next = 1; /* SETUP message */ td->td_flags = htole32(OHCI_TD_SETUP | OHCI_TD_NOCC | OHCI_TD_TOGGLE_0 | OHCI_TD_NOINTR); td->td_cbp = htole32(buf_res.physaddr); buf_offset += (sizeof(usb_device_request_t) - 1); usbd_get_page(&(xfer->buf_data), buf_offset, &buf_res); td->td_be = htole32(buf_res.physaddr); td->len = sizeof(usb_device_request_t); buf_offset += 1; usbd_get_page(&(xfer->buf_data), buf_offset, &buf_res); len -= sizeof(usb_device_request_t); td_last = td; td = td->obj_next; } else { isread = (UE_GET_DIR(xfer->endpoint) == UE_DIR_IN); if(xfer->length == 0) { /* must allow access to "td_last", * so xfer->length cannot be zero! */ printf("%s: setting USBD_FORCE_SHORT_XFER!\n", __FUNCTION__); xfer->flags |= USBD_FORCE_SHORT_XFER; } } td_flags = htole32(OHCI_TD_NOCC | OHCI_TD_NOINTR); if(xfer->flags & USBD_SHORT_XFER_OK) { td_flags |= htole32(OHCI_TD_R); } if(xfer->pipe->toggle_next) { td_flags |= htole32(OHCI_TD_TOGGLE_1); } else { td_flags |= htole32(OHCI_TD_TOGGLE_0); } if(isread) { td_flags |= htole32(OHCI_TD_IN); } else { td_flags |= htole32(OHCI_TD_OUT); } while(1) { if(len == 0) { if(xfer->flags & USBD_FORCE_SHORT_XFER) { if(shortpkt) { break; } } else { break; } } if(len < average) { if((len % xfer->max_packet_size) || (len == 0)) { shortpkt = 1; } average = len; } if(td == NULL) { panic("%s: software wants to write more data " "than there is in the buffer!", __FUNCTION__); } /* link in last TD */ if (td_last) { td_last->td_next = td->td_self; } /* fill out TD */ td->td_flags = td_flags; /* the next TD uses TOGGLE_CARRY */ td_flags &= htole32(~OHCI_TD_TOGGLE_MASK); if(average == 0) { td->td_cbp = 0; td->td_be = ~0; } else { td->td_cbp = htole32(buf_res.physaddr); buf_offset += (average - 1); usbd_get_page(&(xfer->buf_data), buf_offset, &buf_res); td->td_be = htole32(buf_res.physaddr); buf_offset += 1; usbd_get_page(&(xfer->buf_data), buf_offset, &buf_res); } td->len = average; len -= average; td_last = td; td = td->obj_next; } if(xfer->pipe->methods == &ohci_device_ctrl_methods) { /* link in last TD */ td_last->td_next = td->td_self; /* STATUS message */ td->td_flags = htole32(OHCI_TD_NOCC | OHCI_TD_TOGGLE_1 | OHCI_TD_SET_DI(1)); if(isread) { td->td_flags |= htole32(OHCI_TD_OUT); } else { td->td_flags |= htole32(OHCI_TD_IN); } td->td_cbp = 0; td->td_be = ~0; td->len = 0; td_last = td; } td_last->td_next = 0; td_last->td_flags &= htole32(~OHCI_TD_INTR_MASK); td_last->td_flags |= htole32(OHCI_TD_SET_DI(1)); /* must have at least one frame! */ xfer->td_transfer_last = td_last; #ifdef USB_DEBUG if(ohcidebug > 8) { DPRINTF(("nexttog=%d; data before transfer:\n", xfer->pipe->toggle_next)); ohci_dump_tds(xfer->td_start); } #endif ed = xfer->qh_start; ed->ed_flags = htole32(OHCI_ED_FORMAT_GEN | OHCI_ED_DIR_TD); ed->ed_flags |= htole32(OHCI_ED_SET_FA(xfer->address)| OHCI_ED_SET_EN(UE_GET_ADDR(xfer->endpoint))| OHCI_ED_SET_MAXP(xfer->max_packet_size)); if(xfer->udev->speed == USB_SPEED_LOW) { ed->ed_flags |= htole32(OHCI_ED_SPEED); } td = xfer->td_transfer_first; ed->ed_tailp = 0; ed->ed_headp = td->td_self; OHCI_APPEND_QH(ed, *ed_last); if(xfer->pipe->methods == &ohci_device_bulk_methods) { ohci_softc_t *sc = xfer->usb_sc; OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_BLF); } if(xfer->pipe->methods == &ohci_device_ctrl_methods) { ohci_softc_t *sc = xfer->usb_sc; OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_CLF); } return; } static void ohci_root_intr_done(ohci_softc_t *sc, struct usbd_xfer *xfer) { struct usbd_page_search buf_res; u_int8_t *p; u_int16_t i; u_int16_t m; u_int32_t hstatus; if(sc->sc_intrxfer) { /* disable further interrupts */ sc->sc_intrxfer = NULL; /* clear all bits */ usbd_bzero(&(xfer->buf_data), 0, xfer->length); hstatus = OREAD4(sc, OHCI_RH_STATUS); DPRINTF(("sc=%p xfer=%p hstatus=0x%08x\n", sc, xfer, hstatus)); /* set bits */ m = (xfer->length * 8); i = (sc->sc_noport + 1); m = min(m,i); for(i = 1; i < m; i++) { /* pick out CHANGE bits from the status register */ if(OREAD4(sc, OHCI_RH_PORT_STATUS(i)) >> 16) { usbd_get_page(&(xfer->buf_data), i/8, &buf_res); p = buf_res.buffer; *p |= 1 << (i % 8); DPRINTF(("port %d changed\n", i)); } } xfer->actlen = xfer->length; } return; } /* NOTE: "done" can be run two times in a row, * from close and from interrupt */ static void ohci_device_done(struct usbd_xfer *xfer, usbd_status error) { ohci_softc_t *sc = xfer->usb_sc; ohci_ed_t *ed; u_int8_t need_delay; mtx_assert(&sc->sc_bus.mtx, MA_OWNED); need_delay = 0; DPRINTFN(1,("xfer=%p, pipe=%p length=%d error=%d\n", xfer, xfer->pipe, xfer->actlen, error)); for(ed = xfer->qh_start; ed; ed = ed->obj_next) { if((!(ed->ed_flags & htole32(OHCI_ED_SKIP))) && (!(ed->ed_headp & htole32(OHCI_HALTED))) && ((ed->ed_headp ^ ed->ed_tailp) & htole32(-0x10))) { need_delay = 1; } } if(xfer->pipe->methods == &ohci_device_bulk_methods) { OHCI_REMOVE_QH(xfer->qh_start, sc->sc_bulk_p_last); } if(xfer->pipe->methods == &ohci_device_ctrl_methods) { OHCI_REMOVE_QH(xfer->qh_start, sc->sc_ctrl_p_last); } if(xfer->pipe->methods == &ohci_device_intr_methods) { OHCI_REMOVE_QH(xfer->qh_start, sc->sc_intr_p_last[xfer->qh_pos]); } if(xfer->pipe->methods == &ohci_device_isoc_methods) { OHCI_REMOVE_QH(xfer->qh_start, sc->sc_isoc_p_last); } xfer->td_transfer_first = NULL; xfer->td_transfer_last = NULL; /* finish root interrupt transfer * (will update xfer->buffer and xfer->actlen) */ if(xfer->pipe->methods == &ohci_root_intr_methods) { ohci_root_intr_done(sc, xfer); } /* stop timeout */ __callout_stop(&xfer->timeout_handle); /* remove interrupt info */ ohci_remove_interrupt_info(xfer); /* wait until hardware has finished any possible * use of the transfer and QH * * hardware finishes in 1 millisecond */ DELAY(need_delay ? (2*1000) : (5)); if(error) { /* next transfer needs to clear stall */ xfer->pipe->clearstall = 1; } /* transfer is transferred ! */ usbd_transfer_done(xfer,error); /* dequeue transfer (and start next transfer) * * if two transfers are queued, the second * transfer must be started before the * first is called back! */ usbd_transfer_dequeue(xfer); return; } /*---------------------------------------------------------------------------* * ohci bulk support *---------------------------------------------------------------------------*/ static void ohci_device_bulk_open(struct usbd_xfer *xfer) { return; } static void ohci_device_bulk_close(struct usbd_xfer *xfer) { ohci_device_done(xfer, USBD_CANCELLED); return; } static void ohci_device_bulk_enter(struct usbd_xfer *xfer) { /* enqueue transfer */ usbd_transfer_enqueue(xfer); return; } static void ohci_device_bulk_start(struct usbd_xfer *xfer) { ohci_softc_t *sc = xfer->usb_sc; DPRINTFN(3, ("xfer=%p len=%d\n", xfer, xfer->length)); /* setup TD's and QH */ ohci_setup_standard_chain(xfer, &sc->sc_bulk_p_last); /**/ ohci_add_interrupt_info(sc, xfer); if(xfer->timeout && (!(xfer->flags & USBD_USE_POLLING))) { __callout_reset(&xfer->timeout_handle, MS_TO_TICKS(xfer->timeout), (void *)(void *)ohci_timeout, xfer); } return; } struct usbd_pipe_methods ohci_device_bulk_methods = { .open = ohci_device_bulk_open, .close = ohci_device_bulk_close, .enter = ohci_device_bulk_enter, .start = ohci_device_bulk_start, .copy_in = usbd_std_bulk_intr_copy_in, .copy_out = usbd_std_bulk_intr_copy_out, }; /*---------------------------------------------------------------------------* * ohci control support *---------------------------------------------------------------------------*/ static void ohci_device_ctrl_open(struct usbd_xfer *xfer) { return; } static void ohci_device_ctrl_close(struct usbd_xfer *xfer) { ohci_device_done(xfer, USBD_CANCELLED); return; } static void ohci_device_ctrl_enter(struct usbd_xfer *xfer) { /* enqueue transfer */ usbd_transfer_enqueue(xfer); return; } static void ohci_device_ctrl_start(struct usbd_xfer *xfer) { ohci_softc_t *sc = xfer->usb_sc; /* setup TD's and QH */ ohci_setup_standard_chain(xfer, &sc->sc_ctrl_p_last); /**/ ohci_add_interrupt_info(sc, xfer); if(xfer->timeout && (!(xfer->flags & USBD_USE_POLLING))) { __callout_reset(&xfer->timeout_handle, MS_TO_TICKS(xfer->timeout), (void *)(void *)ohci_timeout, xfer); } return; } struct usbd_pipe_methods ohci_device_ctrl_methods = { .open = ohci_device_ctrl_open, .close = ohci_device_ctrl_close, .enter = ohci_device_ctrl_enter, .start = ohci_device_ctrl_start, .copy_in = usbd_std_ctrl_copy_in, .copy_out = usbd_std_ctrl_copy_out, }; /*---------------------------------------------------------------------------* * ohci interrupt support *---------------------------------------------------------------------------*/ static void ohci_device_intr_open(struct usbd_xfer *xfer) { ohci_softc_t *sc = xfer->usb_sc; u_int16_t best; u_int16_t bit; u_int16_t x; best = 0; bit = OHCI_NO_EDS/2; while(bit) { if(xfer->interval >= bit) { x = bit; best = bit; while(x & bit) { if(sc->sc_intr_stat[x] < sc->sc_intr_stat[best]) { best = x; } x++; } break; } bit >>= 1; } sc->sc_intr_stat[best]++; xfer->qh_pos = best; DPRINTFN(2, ("best=%d interval=%d\n", best, xfer->interval)); return; } static void ohci_device_intr_close(struct usbd_xfer *xfer) { ohci_softc_t *sc = xfer->usb_sc; sc->sc_intr_stat[xfer->qh_pos]--; ohci_device_done(xfer,USBD_CANCELLED); return; } static void ohci_device_intr_enter(struct usbd_xfer *xfer) { /* enqueue transfer */ usbd_transfer_enqueue(xfer); return; } static void ohci_device_intr_start(struct usbd_xfer *xfer) { ohci_softc_t *sc = xfer->usb_sc; DPRINTFN(3,("xfer=%p len=%d\n", xfer, xfer->length)); /* setup TD's and QH */ ohci_setup_standard_chain(xfer, &sc->sc_intr_p_last[xfer->qh_pos]); /**/ ohci_add_interrupt_info(sc, xfer); if(xfer->timeout && (!(xfer->flags & USBD_USE_POLLING))) { __callout_reset(&xfer->timeout_handle, MS_TO_TICKS(xfer->timeout), (void *)(void *)ohci_timeout, xfer); } return; } struct usbd_pipe_methods ohci_device_intr_methods = { .open = ohci_device_intr_open, .close = ohci_device_intr_close, .enter = ohci_device_intr_enter, .start = ohci_device_intr_start, .copy_in = usbd_std_bulk_intr_copy_in, .copy_out = usbd_std_bulk_intr_copy_out, }; /*---------------------------------------------------------------------------* * ohci isochronous support *---------------------------------------------------------------------------*/ static void ohci_device_isoc_open(struct usbd_xfer *xfer) { return; } static void ohci_device_isoc_close(struct usbd_xfer *xfer) { /**/ ohci_device_done(xfer, USBD_CANCELLED); return; } static void ohci_device_isoc_enter(struct usbd_xfer *xfer) { struct usbd_page_search buf_res; ohci_softc_t *sc = xfer->usb_sc; u_int32_t buf_offset; u_int32_t nframes; u_int32_t bp0; u_int32_t end_phy; u_int16_t *plen; u_int8_t ncur; u_int8_t allzero = 1; ohci_itd_t *td; ohci_itd_t *td_last = NULL; ohci_ed_t *ed; DPRINTFN(5,("xfer=%p next=%d nframes=%d\n", xfer, xfer->pipe->isoc_next, xfer->nframes)); nframes = le32toh(sc->sc_hw.hcca.hcca_frame_number); if((((nframes - xfer->pipe->isoc_next) & ((1<<16)-1)) < xfer->nframes) || (((xfer->pipe->isoc_next - nframes) & ((1<<16)-1)) >= 256)) { /* not in use yet, schedule it a few frames ahead */ /* data underflow */ xfer->pipe->isoc_next = (nframes + 5) & ((1<<16)-1); DPRINTFN(2,("start next=%d\n", xfer->pipe->isoc_next)); } nframes = xfer->nframes; if(nframes == 0) { /* transfer transferred */ ohci_device_done(xfer, USBD_NORMAL_COMPLETION); /* call callback recursively */ __usbd_callback(xfer); return; } buf_offset = 0; usbd_get_page(&(xfer->buf_data), buf_offset, &buf_res); plen = xfer->frlengths; td = xfer->td_start; xfer->td_transfer_first = td; ncur = 0; bp0 = OHCI_PAGE(buf_res.physaddr); end_phy = 0; while(nframes--) { if(td == NULL) { panic("%s:%d: out of TD's\n", __FUNCTION__, __LINE__); } td->itd_offset[ncur] = htole16(OHCI_ITD_MK_OFFS (buf_res.physaddr - bp0)); if (*plen) { allzero = 0; buf_offset += (*plen) -1; usbd_get_page(&(xfer->buf_data), buf_offset, &buf_res); end_phy = buf_res.physaddr; buf_offset += 1; usbd_get_page(&(xfer->buf_data), buf_offset, &buf_res); } plen++; ncur++; if((ncur == OHCI_ITD_NOFFSET) || (OHCI_PAGE(buf_res.physaddr) != bp0) || (nframes == 0)) { /* link in last TD */ if (td_last) { td_last->itd_next = td->itd_self; } /* fill current ITD */ td->itd_flags = htole32( OHCI_ITD_NOCC | OHCI_ITD_SET_SF(xfer->pipe->isoc_next) | OHCI_ITD_NOINTR | OHCI_ITD_SET_FC(ncur)); if (allzero) { td->itd_bp0 = 0; td->itd_be = ~0; } else { td->itd_bp0 = htole32(bp0); td->itd_be = htole32(end_phy); } td->frames = ncur; xfer->pipe->isoc_next += ncur; bp0 = OHCI_PAGE(buf_res.physaddr); ncur = 0; allzero = 1; td_last = td; td = td->obj_next; } } /* update the last TD */ td_last->itd_flags &= htole32(~OHCI_ITD_NOINTR); td_last->itd_flags |= htole32(OHCI_ITD_SET_DI(0)); td_last->itd_next = 0; xfer->td_transfer_last = td_last; #ifdef USB_DEBUG if(ohcidebug > 8) { DPRINTF(("data before transfer:\n")); ohci_dump_itds(xfer->td_start); } #endif ed = xfer->qh_start; if(UE_GET_DIR(xfer->endpoint) == UE_DIR_IN) ed->ed_flags = htole32(OHCI_ED_DIR_IN|OHCI_ED_FORMAT_ISO); else ed->ed_flags = htole32(OHCI_ED_DIR_OUT|OHCI_ED_FORMAT_ISO); ed->ed_flags |= htole32(OHCI_ED_SET_FA(xfer->address)| OHCI_ED_SET_EN(UE_GET_ADDR(xfer->endpoint))| OHCI_ED_SET_MAXP(xfer->max_packet_size)); if(xfer->udev->speed == USB_SPEED_LOW) { ed->ed_flags |= htole32(OHCI_ED_SPEED); } ed->ed_tailp = 0; td = xfer->td_transfer_first; ed->ed_headp = td->itd_self; OHCI_APPEND_QH(ed, sc->sc_isoc_p_last); /**/ ohci_add_interrupt_info(sc, xfer); if(!xfer->timeout) { /* in case the frame start number is wrong */ xfer->timeout = 1000/4; } if(xfer->timeout && (!(xfer->flags & USBD_USE_POLLING))) { __callout_reset(&xfer->timeout_handle, MS_TO_TICKS(xfer->timeout), (void *)(void *)ohci_timeout, xfer); } /* enqueue transfer * (so that it can be aborted through pipe abort) */ usbd_transfer_enqueue(xfer); return; } static void ohci_device_isoc_start(struct usbd_xfer *xfer) { /* already started, nothing to do */ return; } struct usbd_pipe_methods ohci_device_isoc_methods = { .open = ohci_device_isoc_open, .close = ohci_device_isoc_close, .enter = ohci_device_isoc_enter, .start = ohci_device_isoc_start, .copy_in = usbd_std_isoc_copy_in, .copy_out = usbd_std_isoc_copy_out, }; /*---------------------------------------------------------------------------* * ohci root control support *---------------------------------------------------------------------------* * simulate a hardware hub by handling * all the necessary requests *---------------------------------------------------------------------------*/ static void ohci_root_ctrl_open(struct usbd_xfer *xfer) { return; } static void ohci_root_ctrl_close(struct usbd_xfer *xfer) { ohci_device_done(xfer,USBD_CANCELLED); return; } /* data structures and routines * to emulate the root hub: */ static const usb_device_descriptor_t ohci_devd = { sizeof(usb_device_descriptor_t), UDESC_DEVICE, /* type */ {0x00, 0x01}, /* USB version */ UDCLASS_HUB, /* class */ UDSUBCLASS_HUB, /* subclass */ UDPROTO_FSHUB, /* protocol */ 64, /* max packet */ {0},{0},{0x00,0x01}, /* device id */ 1,2,0, /* string indicies */ 1 /* # of configurations */ }; static const usb_config_descriptor_t ohci_confd = { sizeof(usb_config_descriptor_t), UDESC_CONFIG, {USB_CONFIG_DESCRIPTOR_SIZE + USB_INTERFACE_DESCRIPTOR_SIZE + USB_ENDPOINT_DESCRIPTOR_SIZE}, 1, 1, 0, UC_SELF_POWERED, 0 /* max power */ }; static const usb_interface_descriptor_t ohci_ifcd = { sizeof(usb_interface_descriptor_t), UDESC_INTERFACE, 0, 0, 1, UICLASS_HUB, UISUBCLASS_HUB, UIPROTO_FSHUB, 0 }; static const usb_endpoint_descriptor_t ohci_endpd = { sizeof(usb_endpoint_descriptor_t), UDESC_ENDPOINT, UE_DIR_IN | OHCI_INTR_ENDPT, UE_INTERRUPT, {8, 0}, /* max packet */ 255 }; static const usb_hub_descriptor_t ohci_hubd = { 0, /* dynamic length */ UDESC_HUB, 0, {0,0}, 0, 0, {0}, }; static void ohci_root_ctrl_enter(struct usbd_xfer *xfer) { ohci_softc_t *sc = xfer->usb_sc; u_int32_t port; u_int32_t v; u_int16_t len; u_int16_t value; u_int16_t index; u_int16_t l; u_int16_t totlen = 0; u_int16_t i; union { usb_status_t stat; usb_port_status_t ps; usb_device_request_t req; usb_device_descriptor_t devd; usb_hub_descriptor_t hubd; u_int8_t str_temp[128]; u_int8_t byte_temp; } u; usbd_status err; mtx_assert(&sc->sc_bus.mtx, MA_OWNED); if (xfer->length < sizeof(u.req)) { err = USBD_INVAL; goto done; } /* set default actual length */ xfer->actlen = sizeof(u.req); /* copy out "request" */ usbd_copy_out(&(xfer->buf_data), 0, &u.req, sizeof(u.req)); len = (xfer->length - sizeof(u.req)); if (len != UGETW(u.req.wLength)) { err = USBD_INVAL; goto done; } value = UGETW(u.req.wValue); index = UGETW(u.req.wIndex); DPRINTFN(2,("type=0x%02x request=0x%02x wLen=0x%04x " "wValue=0x%04x wIndex=0x%04x\n", u.req.bmRequestType, u.req.bRequest, len, value, index)); #define C(x,y) ((x) | ((y) << 8)) switch(C(u.req.bRequest, u.req.bmRequestType)) { case C(UR_CLEAR_FEATURE, UT_WRITE_DEVICE): case C(UR_CLEAR_FEATURE, UT_WRITE_INTERFACE): case C(UR_CLEAR_FEATURE, UT_WRITE_ENDPOINT): /* * DEVICE_REMOTE_WAKEUP and ENDPOINT_HALT are no-ops * for the integrated root hub. */ break; case C(UR_GET_CONFIG, UT_READ_DEVICE): if(len > 0) { u.byte_temp = sc->sc_conf; totlen = 1; usbd_copy_in(&(xfer->buf_data), sizeof(u.req), &u, totlen); } break; case C(UR_GET_DESCRIPTOR, UT_READ_DEVICE): switch(value >> 8) { case UDESC_DEVICE: if((value & 0xff) != 0) { err = USBD_IOERROR; goto done; } totlen = min(len, sizeof(u.devd)); u.devd = ohci_devd; #if 0 USETW(u.devd.idVendor, sc->sc_id_vendor); #endif usbd_copy_in(&(xfer->buf_data), sizeof(u.req), &u, totlen); break; case UDESC_CONFIG: if((value & 0xff) != 0) { err = USBD_IOERROR; goto done; } totlen = l = min(len, sizeof(ohci_confd)); len -= l; usbd_copy_in(&(xfer->buf_data), sizeof(u.req), &ohci_confd, l); l = min(len, sizeof(ohci_ifcd)); totlen += l; len -= l; usbd_copy_in(&(xfer->buf_data), sizeof(u.req) + sizeof(ohci_confd), &ohci_ifcd, l); l = min(len, sizeof(ohci_endpd)); totlen += l; len -= l; usbd_copy_in(&(xfer->buf_data), sizeof(u.req) + sizeof(ohci_confd) + sizeof(ohci_ifcd), &ohci_endpd, l); break; case UDESC_STRING: if(len == 0) { break; } switch (value & 0xff) { case 0: /* Language table */ totlen = usbd_make_str_desc (u.str_temp, sizeof(u.str_temp), "\001"); break; case 1: /* Vendor */ totlen = usbd_make_str_desc (u.str_temp, sizeof(u.str_temp), sc->sc_vendor); break; case 2: /* Product */ totlen = usbd_make_str_desc (u.str_temp, sizeof(u.str_temp), "OHCI root hub"); break; default: totlen = usbd_make_str_desc (u.str_temp, sizeof(u.str_temp), ""); break; } if (totlen > len) { totlen = len; } usbd_copy_in(&(xfer->buf_data), sizeof(u.req), &u, totlen); break; default: err = USBD_IOERROR; goto done; } break; case C(UR_GET_INTERFACE, UT_READ_INTERFACE): if(len > 0) { u.byte_temp = 0; totlen = 1; usbd_copy_in(&(xfer->buf_data), sizeof(u.req), &u, totlen); } break; case C(UR_GET_STATUS, UT_READ_DEVICE): if(len > 1) { USETW(u.stat.wStatus,UDS_SELF_POWERED); totlen = 2; usbd_copy_in(&(xfer->buf_data), sizeof(u.req), &u, totlen); } break; case C(UR_GET_STATUS, UT_READ_INTERFACE): case C(UR_GET_STATUS, UT_READ_ENDPOINT): if(len > 1) { USETW(u.stat.wStatus, 0); totlen = 2; usbd_copy_in(&(xfer->buf_data), sizeof(u.req), &u, totlen); } break; case C(UR_SET_ADDRESS, UT_WRITE_DEVICE): if(value >= USB_MAX_DEVICES) { err = USBD_IOERROR; goto done; } sc->sc_addr = value; break; case C(UR_SET_CONFIG, UT_WRITE_DEVICE): if((value != 0) && (value != 1)) { err = USBD_IOERROR; goto done; } sc->sc_conf = value; break; case C(UR_SET_DESCRIPTOR, UT_WRITE_DEVICE): break; case C(UR_SET_FEATURE, UT_WRITE_DEVICE): case C(UR_SET_FEATURE, UT_WRITE_INTERFACE): case C(UR_SET_FEATURE, UT_WRITE_ENDPOINT): err = USBD_IOERROR; goto done; case C(UR_SET_INTERFACE, UT_WRITE_INTERFACE): break; case C(UR_SYNCH_FRAME, UT_WRITE_ENDPOINT): break; /* Hub requests */ case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_DEVICE): break; case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_OTHER): DPRINTFN(8, ("UR_CLEAR_PORT_FEATURE " "port=%d feature=%d\n", index, value)); if((index < 1) || (index > sc->sc_noport)) { err = USBD_IOERROR; goto done; } port = OHCI_RH_PORT_STATUS(index); switch(value) { case UHF_PORT_ENABLE: OWRITE4(sc, port, UPS_CURRENT_CONNECT_STATUS); break; case UHF_PORT_SUSPEND: OWRITE4(sc, port, UPS_OVERCURRENT_INDICATOR); break; case UHF_PORT_POWER: /* Yes, writing to the LOW_SPEED bit clears power. */ OWRITE4(sc, port, UPS_LOW_SPEED); break; case UHF_C_PORT_CONNECTION: OWRITE4(sc, port, UPS_C_CONNECT_STATUS << 16); break; case UHF_C_PORT_ENABLE: OWRITE4(sc, port, UPS_C_PORT_ENABLED << 16); break; case UHF_C_PORT_SUSPEND: OWRITE4(sc, port, UPS_C_SUSPEND << 16); break; case UHF_C_PORT_OVER_CURRENT: OWRITE4(sc, port, UPS_C_OVERCURRENT_INDICATOR << 16); break; case UHF_C_PORT_RESET: OWRITE4(sc, port, UPS_C_PORT_RESET << 16); break; default: err = USBD_IOERROR; goto done; } switch(value) { case UHF_C_PORT_CONNECTION: case UHF_C_PORT_ENABLE: case UHF_C_PORT_SUSPEND: case UHF_C_PORT_OVER_CURRENT: case UHF_C_PORT_RESET: /* enable RHSC interrupt if condition is cleared. */ if((OREAD4(sc, port) >> 16) == 0) { mtx_lock(&sc->sc_bus.mtx); ohci_rhsc_enable(sc); } break; default: break; } break; case C(UR_GET_DESCRIPTOR, UT_READ_CLASS_DEVICE): if((value & 0xff) != 0) { err = USBD_IOERROR; goto done; } v = OREAD4(sc, OHCI_RH_DESCRIPTOR_A); u.hubd = ohci_hubd; u.hubd.bNbrPorts = sc->sc_noport; USETW(u.hubd.wHubCharacteristics, (v & OHCI_NPS ? UHD_PWR_NO_SWITCH : v & OHCI_PSM ? UHD_PWR_GANGED : UHD_PWR_INDIVIDUAL) /* XXX overcurrent */ ); u.hubd.bPwrOn2PwrGood = OHCI_GET_POTPGT(v); v = OREAD4(sc, OHCI_RH_DESCRIPTOR_B); for(l = 0; l < sc->sc_noport; l++) { if (v & 1) { u.hubd.DeviceRemovable[l/8] |= (1 << (l % 8)); } v >>= 1; } u.hubd.bDescLength = (USB_HUB_DESCRIPTOR_SIZE-1) + ((sc->sc_noport + 7)/8); totlen = min(len, u.hubd.bDescLength); usbd_copy_in(&(xfer->buf_data), sizeof(u.req), &u, totlen); break; case C(UR_GET_STATUS, UT_READ_CLASS_DEVICE): if(len < 4) { err = USBD_IOERROR; goto done; } usbd_bzero(&(xfer->buf_data), sizeof(u.req), len); totlen = len; break; case C(UR_GET_STATUS, UT_READ_CLASS_OTHER): DPRINTFN(8,("get port status i=%d\n", index)); if((index < 1) || (index > sc->sc_noport)) { err = USBD_IOERROR; goto done; } if(len < 4) { err = USBD_IOERROR; goto done; } v = OREAD4(sc, OHCI_RH_PORT_STATUS(index)); DPRINTFN(8,("port status=0x%04x\n", v)); USETW(u.ps.wPortStatus, v); USETW(u.ps.wPortChange, v >> 16); totlen = min(len, sizeof(u.ps)); usbd_copy_in(&(xfer->buf_data), sizeof(u.req), &u, totlen); break; case C(UR_SET_DESCRIPTOR, UT_WRITE_CLASS_DEVICE): err = USBD_IOERROR; goto done; case C(UR_SET_FEATURE, UT_WRITE_CLASS_DEVICE): break; case C(UR_SET_FEATURE, UT_WRITE_CLASS_OTHER): if((index < 1) || (index > sc->sc_noport)) { err = USBD_IOERROR; goto done; } port = OHCI_RH_PORT_STATUS(index); switch(value) { case UHF_PORT_ENABLE: OWRITE4(sc, port, UPS_PORT_ENABLED); break; case UHF_PORT_SUSPEND: OWRITE4(sc, port, UPS_SUSPEND); break; case UHF_PORT_RESET: DPRINTFN(5,("reset port %d\n", index)); OWRITE4(sc, port, UPS_RESET); for(i = 0; i < 5; i++) { DELAY(1000*USB_PORT_ROOT_RESET_DELAY); if((OREAD4(sc, port) & UPS_RESET) == 0) { break; } } DPRINTFN(8,("ohci port %d reset, status = 0x%04x\n", index, OREAD4(sc, port))); break; case UHF_PORT_POWER: DPRINTFN(2,("set port power %d\n", index)); OWRITE4(sc, port, UPS_PORT_POWER); break; default: err = USBD_IOERROR; goto done; } break; default: err = USBD_IOERROR; goto done; } xfer->actlen = totlen + sizeof(u.req); err = USBD_NORMAL_COMPLETION; done: /* transfer transferred */ ohci_device_done(xfer,err); /* call callback recursively */ __usbd_callback(xfer); return; } static void ohci_root_ctrl_start(struct usbd_xfer *xfer) { /* not used */ return; } struct usbd_pipe_methods ohci_root_ctrl_methods = { .open = ohci_root_ctrl_open, .close = ohci_root_ctrl_close, .enter = ohci_root_ctrl_enter, .start = ohci_root_ctrl_start, .copy_in = usbd_std_ctrl_copy_in, .copy_out = usbd_std_ctrl_copy_out, }; /*---------------------------------------------------------------------------* * ohci root interrupt support *---------------------------------------------------------------------------*/ static void ohci_root_intr_open(struct usbd_xfer *xfer) { return; } static void ohci_root_intr_close(struct usbd_xfer *xfer) { ohci_device_done(xfer, USBD_CANCELLED); return; } static void ohci_root_intr_enter(struct usbd_xfer *xfer) { DPRINTFN(3, ("xfer=%p len=%d\n", xfer, xfer->length)); /* enqueue transfer * (so that it can be aborted through pipe abort) */ usbd_transfer_enqueue(xfer); return; } static void ohci_root_intr_start(struct usbd_xfer *xfer) { ohci_softc_t *sc = xfer->usb_sc; /* only one transfer at a time * (sc_intrxfer is cleared by ohci_root_intr_done()) */ sc->sc_intrxfer = xfer; return; } struct usbd_pipe_methods ohci_root_intr_methods = { .open = ohci_root_intr_open, .close = ohci_root_intr_close, .enter = ohci_root_intr_enter, .start = ohci_root_intr_start, .copy_in = usbd_std_bulk_intr_copy_in, .copy_out = usbd_std_bulk_intr_copy_out, }; #define ADD_BYTES(ptr,size) ((void *)(((u_int8_t *)(ptr)) + (size))) static usbd_status ohci_xfer_setup(struct usbd_device *udev, u_int8_t iface_index, struct usbd_xfer **pxfer, const struct usbd_config *setup_start, const struct usbd_config *setup_end) { enum { max_frames = 128 }; ohci_softc_t *sc = OHCI_BUS2SC(udev->bus); const struct usbd_config *setup; struct usbd_memory_info *info; struct usbd_page *page_ptr; struct usbd_xfer dummy; struct usbd_xfer *xfer; u_int32_t size[2]; u_int32_t total_size[2]; u_int32_t ntd; u_int32_t nitd; u_int32_t nqh; u_int32_t n; void *buf; void *temp_ptr; void *last_obj; bus_size_t temp_phy; usbd_status error = 0; buf = NULL; page_ptr = NULL; total_size[0] = 0; total_size[1] = 0; repeat: size[0] = 0; size[1] = 0; /* align data to 8 byte boundary */ size[0] += ((-size[0]) & (USB_HOST_ALIGN-1)); if (buf) { info = ADD_BYTES(buf,size[0]); info->memory_base = buf; info->memory_size = total_size[0]; info->page_base = page_ptr; info->page_size = total_size[1]; info->usb_mtx = &sc->sc_bus.mtx; } else { info = NULL; } size[0] += sizeof(info[0]); for(setup = setup_start; setup < setup_end; setup++) { ntd = 0; nitd = 0; nqh = 0; /* align data to 8 byte boundary */ size[0] += ((-size[0]) & (USB_HOST_ALIGN-1)); if(buf) { xfer = ADD_BYTES(buf,size[0]); *pxfer++ = xfer; } else { /* need dummy xfer to * calculate ntd and nqh ! */ xfer = &dummy; bzero(&dummy, sizeof(dummy)); } /* * setup xfer */ xfer->usb_sc = sc; xfer->usb_mtx = &sc->sc_bus.mtx; xfer->usb_root = info; xfer->address = udev->address; xfer->pipe = usbd_get_pipe(udev, iface_index, setup); if(!xfer->pipe) { error = USBD_NO_PIPE; DPRINTF(("no pipe for endpoint %d\n", setup->endpoint)); goto done; } else { usbd_std_transfer_setup(xfer, setup, 0x500, 0x500); /* * calculate ntd and nqh */ if((xfer->pipe->methods == &ohci_device_ctrl_methods) || (xfer->pipe->methods == &ohci_device_bulk_methods) || (xfer->pipe->methods == &ohci_device_intr_methods)) { nitd = 0; ntd = (1+ /* SETUP */ 1+ /* STATUS */ 1+ /* SHORTPKT */ 1 /* EXTRA */) + (xfer->length / (OHCI_PAGE_SIZE/2)) /* DATA */; } else if(xfer->pipe->methods == &ohci_device_isoc_methods) { if(xfer->nframes == 0) { error = USBD_ZERO_FRAMES_IN_ISOC_MODE; DPRINTF(("frames == 0 in isochronous mode; " "endpoint 0x%02x\n", setup->endpoint)); goto done; } if(xfer->nframes >= max_frames) { error = USBD_INVAL; DPRINTF(("isochronous frame-limit " "exceeded by 0x%x frames; endpoint 0x%02x\n", setup->frames - max_frames, setup->endpoint)); goto done; } nitd = ((xfer->length / OHCI_PAGE_SIZE) + ((xfer->nframes + OHCI_ITD_NOFFSET - 1) / OHCI_ITD_NOFFSET) + 1 /* EXTRA */); ntd = 0; } else { nitd = 0; ntd = 0; } if((xfer->pipe->methods == &ohci_device_ctrl_methods) || (xfer->pipe->methods == &ohci_device_bulk_methods) || (xfer->pipe->methods == &ohci_device_intr_methods) || (xfer->pipe->methods == &ohci_device_isoc_methods)) { nqh = 1; } else { nqh = 0; } } size[0] += sizeof(xfer[0]); if(xfer->nframes) { /* align data to 8 byte boundary */ size[0] += ((-size[0]) & (USB_HOST_ALIGN-1)); xfer->frlengths = ADD_BYTES(buf,size[0]); size[0] += (xfer->nframes * sizeof(xfer->frlengths[0])); xfer->frlengths_old = ADD_BYTES(buf,size[0]); size[0] += (xfer->nframes * sizeof(xfer->frlengths[0])); } if (!(xfer->flags & USBD_USE_DMA)) { /* align data to 8 byte boundary */ size[0] += ((-size[0]) & (USB_HOST_ALIGN-1)); xfer->buffer = ADD_BYTES(buf,size[0]); size[0] += xfer->length; } /*****/ /* align data to 8 byte boundary */ size[1] += ((-size[1]) & (USB_HOST_ALIGN-1)); usbd_page_set_start(&(xfer->buf_data), page_ptr, size[1]); size[1] += xfer->length; usbd_page_set_end(&(xfer->buf_data), page_ptr, size[1]); /* align data */ size[1] += ((-size[1]) & (OHCI_ITD_ALIGN-1)); last_obj = NULL; for(n = 0; n < ntd; n++) { size[1] += usbd_page_fit_obj(page_ptr, size[1], sizeof(ohci_td_t)); if(buf) { temp_ptr = usbd_page_get_buf(page_ptr,size[1]); temp_phy = usbd_page_get_phy(page_ptr,size[1]); /* init TD */ ((ohci_td_t *)temp_ptr)->td_self = htole32(temp_phy); ((ohci_td_t *)temp_ptr)->obj_next = last_obj; last_obj = temp_ptr; } size[1] += sizeof(ohci_td_t); } for(n = 0; n < nitd; n++) { size[1] += usbd_page_fit_obj(page_ptr, size[1], sizeof(ohci_itd_t)); if(buf) { temp_ptr = usbd_page_get_buf(page_ptr,size[1]); temp_phy = usbd_page_get_phy(page_ptr,size[1]); /* init TD */ ((ohci_itd_t *)temp_ptr)->itd_self = htole32(temp_phy); ((ohci_itd_t *)temp_ptr)->obj_next = last_obj; last_obj = temp_ptr; } size[1] += sizeof(ohci_itd_t); } xfer->td_start = last_obj; /* align data */ size[1] += ((-size[1]) & (OHCI_ED_ALIGN-1)); last_obj = NULL; for(n = 0; n < nqh; n++) { size[1] += usbd_page_fit_obj(page_ptr, size[1], sizeof(ohci_ed_t)); if(buf) { temp_ptr = usbd_page_get_buf(page_ptr,size[1]); temp_phy = usbd_page_get_phy(page_ptr,size[1]); /* init QH */ ((ohci_ed_t *)temp_ptr)->ed_self = htole32(temp_phy); ((ohci_ed_t *)temp_ptr)->obj_next = last_obj; last_obj = temp_ptr; } size[1] += sizeof(ohci_ed_t); } xfer->qh_start = last_obj; } if (buf || error) { goto done; } /* compute number of USB pages required */ total_size[1] = (size[1] + USB_PAGE_SIZE - 1) / USB_PAGE_SIZE; /* align data to 8 byte boundary */ size[0] += ((-size[0]) & (USB_HOST_ALIGN-1)); /* store offset temporarily */ n = size[0]; size[0] += (sizeof(*page_ptr) * total_size[1]); /* store total buffer size */ total_size[0] = size[0]; /* allocate zeroed memory */ buf = malloc(total_size[0], M_USB, M_WAITOK|M_ZERO); if (buf == NULL) { error = USBD_NOMEM; DPRINTF(("cannot allocate memory block for " "configuration (%d bytes)\n", total_size[0])); goto done; } page_ptr = ADD_BYTES(buf,n); if (usbd_page_alloc(sc->sc_bus.dma_tag, page_ptr, total_size[1])) { free(buf, M_USB); error = USBD_NOMEM; DPRINTF(("cannot allocate memory block for " "configuration (%d USB pages)\n", total_size[1])); goto done; } goto repeat; done: return error; } static void ohci_pipe_init(struct usbd_device *udev, usb_endpoint_descriptor_t *edesc, struct usbd_pipe *pipe) { ohci_softc_t *sc = OHCI_BUS2SC(udev->bus); DPRINTFN(1, ("pipe=%p, addr=%d, endpt=%d (%d)\n", pipe, udev->address, edesc->bEndpointAddress, sc->sc_addr)); if(udev->address == sc->sc_addr) { switch (edesc->bEndpointAddress) { case USB_CONTROL_ENDPOINT: pipe->methods = &ohci_root_ctrl_methods; break; case UE_DIR_IN | OHCI_INTR_ENDPT: pipe->methods = &ohci_root_intr_methods; break; default: panic("invalid endpoint address: 0x%02x", edesc->bEndpointAddress); break; } } else { switch (edesc->bmAttributes & UE_XFERTYPE) { case UE_CONTROL: pipe->methods = &ohci_device_ctrl_methods; break; case UE_INTERRUPT: pipe->methods = &ohci_device_intr_methods; break; case UE_ISOCHRONOUS: pipe->methods = &ohci_device_isoc_methods; break; case UE_BULK: pipe->methods = &ohci_device_bulk_methods; break; } } return; } struct usbd_bus_methods ohci_bus_methods = { .pipe_init = ohci_pipe_init, .xfer_setup = ohci_xfer_setup, .do_poll = ohci_do_poll, }; pwcbsd/usb/ohci.h000644 000423 000000 00000027144 10551670753 014503 0ustar00luigiwheel000000 000000 /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifndef _OHCI_H_ #define _OHCI_H_ /* PCI config registers */ #define PCI_CBMEM 0x10 /* configuration base memory */ #define PCI_INTERFACE_OHCI 0x10 /* OHCI registers */ #define OHCI_REVISION 0x00 /* OHCI revision */ #define OHCI_REV_LO(rev) ((rev) & 0xf) #define OHCI_REV_HI(rev) (((rev)>>4) & 0xf) #define OHCI_REV_LEGACY(rev) ((rev) & 0x100) #define OHCI_CONTROL 0x04 #define OHCI_CBSR_MASK 0x00000003 /* Control/Bulk Service Ratio */ #define OHCI_RATIO_1_1 0x00000000 #define OHCI_RATIO_1_2 0x00000001 #define OHCI_RATIO_1_3 0x00000002 #define OHCI_RATIO_1_4 0x00000003 #define OHCI_PLE 0x00000004 /* Periodic List Enable */ #define OHCI_IE 0x00000008 /* Isochronous Enable */ #define OHCI_CLE 0x00000010 /* Control List Enable */ #define OHCI_BLE 0x00000020 /* Bulk List Enable */ #define OHCI_HCFS_MASK 0x000000c0 /* HostControllerFunctionalState */ #define OHCI_HCFS_RESET 0x00000000 #define OHCI_HCFS_RESUME 0x00000040 #define OHCI_HCFS_OPERATIONAL 0x00000080 #define OHCI_HCFS_SUSPEND 0x000000c0 #define OHCI_IR 0x00000100 /* Interrupt Routing */ #define OHCI_RWC 0x00000200 /* Remote Wakeup Connected */ #define OHCI_RWE 0x00000400 /* Remote Wakeup Enabled */ #define OHCI_COMMAND_STATUS 0x08 #define OHCI_HCR 0x00000001 /* Host Controller Reset */ #define OHCI_CLF 0x00000002 /* Control List Filled */ #define OHCI_BLF 0x00000004 /* Bulk List Filled */ #define OHCI_OCR 0x00000008 /* Ownership Change Request */ #define OHCI_SOC_MASK 0x00030000 /* Scheduling Overrun Count */ #define OHCI_INTERRUPT_STATUS 0x0c #define OHCI_SO 0x00000001 /* Scheduling Overrun */ #define OHCI_WDH 0x00000002 /* Writeback Done Head */ #define OHCI_SF 0x00000004 /* Start of Frame */ #define OHCI_RD 0x00000008 /* Resume Detected */ #define OHCI_UE 0x00000010 /* Unrecoverable Error */ #define OHCI_FNO 0x00000020 /* Frame Number Overflow */ #define OHCI_RHSC 0x00000040 /* Root Hub Status Change */ #define OHCI_OC 0x40000000 /* Ownership Change */ #define OHCI_MIE 0x80000000 /* Master Interrupt Enable */ #define OHCI_INTERRUPT_ENABLE 0x10 #define OHCI_INTERRUPT_DISABLE 0x14 #define OHCI_HCCA 0x18 #define OHCI_PERIOD_CURRENT_ED 0x1c #define OHCI_CONTROL_HEAD_ED 0x20 #define OHCI_CONTROL_CURRENT_ED 0x24 #define OHCI_BULK_HEAD_ED 0x28 #define OHCI_BULK_CURRENT_ED 0x2c #define OHCI_DONE_HEAD 0x30 #define OHCI_FM_INTERVAL 0x34 #define OHCI_GET_IVAL(s) ((s) & 0x3fff) #define OHCI_GET_FSMPS(s) (((s) >> 16) & 0x7fff) #define OHCI_FIT 0x80000000 #define OHCI_FM_REMAINING 0x38 #define OHCI_FM_NUMBER 0x3c #define OHCI_PERIODIC_START 0x40 #define OHCI_LS_THRESHOLD 0x44 #define OHCI_RH_DESCRIPTOR_A 0x48 #define OHCI_GET_NDP(s) ((s) & 0xff) #define OHCI_PSM 0x0100 /* Power Switching Mode */ #define OHCI_NPS 0x0200 /* No Power Switching */ #define OHCI_DT 0x0400 /* Device Type */ #define OHCI_OCPM 0x0800 /* Overcurrent Protection Mode */ #define OHCI_NOCP 0x1000 /* No Overcurrent Protection */ #define OHCI_GET_POTPGT(s) ((s) >> 24) #define OHCI_RH_DESCRIPTOR_B 0x4c #define OHCI_RH_STATUS 0x50 #define OHCI_LPS 0x00000001 /* Local Power Status */ #define OHCI_OCI 0x00000002 /* OverCurrent Indicator */ #define OHCI_DRWE 0x00008000 /* Device Remote Wakeup Enable */ #define OHCI_LPSC 0x00010000 /* Local Power Status Change */ #define OHCI_CCIC 0x00020000 /* OverCurrent Indicator Change */ #define OHCI_CRWE 0x80000000 /* Clear Remote Wakeup Enable */ #define OHCI_RH_PORT_STATUS(n) (0x50 + ((n)*4)) /* 1 based indexing */ #define OHCI_LES (OHCI_PLE | OHCI_IE | OHCI_CLE | OHCI_BLE) #define OHCI_ALL_INTRS (OHCI_SO | OHCI_WDH | OHCI_SF | \ OHCI_RD | OHCI_UE | OHCI_FNO | \ OHCI_RHSC | OHCI_OC) #define OHCI_NORMAL_INTRS (OHCI_WDH | OHCI_RD | OHCI_UE | OHCI_RHSC) #define OHCI_FSMPS(i) (((i-210)*6/7) << 16) #define OHCI_PERIODIC(i) ((i)*9/10) #define OHCI_NO_INTRS 32 #define OHCI_HCCA_SIZE 256 /* Structures alignment (bytes) */ #define OHCI_HCCA_ALIGN 256 #define OHCI_ED_ALIGN 16 #define OHCI_TD_ALIGN 16 #define OHCI_ITD_ALIGN 32 #define OHCI_PAGE_SIZE 0x1000 #define OHCI_PAGE(x) ((x) &~ 0xfff) #define OHCI_PAGE_OFFSET(x) ((x) & 0xfff) #define OHCI_PAGE_MASK(x) ((x) & 0xfff) #if ((USB_PAGE_SIZE < OHCI_ED_ALIGN) || (OHCI_ED_ALIGN == 0) || \ (USB_PAGE_SIZE < OHCI_TD_ALIGN) || (OHCI_TD_ALIGN == 0) || \ (USB_PAGE_SIZE < OHCI_ITD_ALIGN) || (OHCI_ITD_ALIGN == 0) || \ (USB_PAGE_SIZE < OHCI_PAGE_SIZE) || (OHCI_PAGE_SIZE == 0)) #error "Invalid USB page size!" #endif struct ohci_hcca { volatile uint32_t hcca_interrupt_table[OHCI_NO_INTRS]; volatile uint32_t hcca_frame_number; volatile uint32_t hcca_done_head; #define OHCI_DONE_INTRS 1 } __aligned(OHCI_HCCA_ALIGN); typedef struct ohci_ed { volatile uint32_t ed_flags; #define OHCI_ED_GET_FA(s) ((s) & 0x7f) #define OHCI_ED_ADDRMASK 0x0000007f #define OHCI_ED_SET_FA(s) (s) #define OHCI_ED_GET_EN(s) (((s) >> 7) & 0xf) #define OHCI_ED_SET_EN(s) ((s) << 7) #define OHCI_ED_DIR_MASK 0x00001800 #define OHCI_ED_DIR_TD 0x00000000 #define OHCI_ED_DIR_OUT 0x00000800 #define OHCI_ED_DIR_IN 0x00001000 #define OHCI_ED_SPEED 0x00002000 #define OHCI_ED_SKIP 0x00004000 #define OHCI_ED_FORMAT_GEN 0x00000000 #define OHCI_ED_FORMAT_ISO 0x00008000 #define OHCI_ED_GET_MAXP(s) (((s) >> 16) & 0x07ff) #define OHCI_ED_SET_MAXP(s) ((s) << 16) #define OHCI_ED_MAXPMASK (0x7ff << 16) volatile uint32_t ed_tailp; volatile uint32_t ed_headp; #define OHCI_HALTED 0x00000001 #define OHCI_TOGGLECARRY 0x00000002 #define OHCI_HEADMASK 0xfffffffc volatile uint32_t ed_next; /* * Extra information needed: */ struct ohci_ed *next; struct ohci_ed *prev; struct ohci_ed *obj_next; uint32_t ed_self; } __aligned(OHCI_ED_ALIGN) ohci_ed_t; typedef struct ohci_td { volatile uint32_t td_flags; #define OHCI_TD_R 0x00040000 /* Buffer Rounding */ #define OHCI_TD_DP_MASK 0x00180000 /* Direction / PID */ #define OHCI_TD_SETUP 0x00000000 #define OHCI_TD_OUT 0x00080000 #define OHCI_TD_IN 0x00100000 #define OHCI_TD_GET_DI(x) (((x) >> 21) & 7) /* Delay Interrupt */ #define OHCI_TD_SET_DI(x) ((x) << 21) #define OHCI_TD_NOINTR 0x00e00000 #define OHCI_TD_INTR_MASK 0x00e00000 #define OHCI_TD_TOGGLE_CARRY 0x00000000 #define OHCI_TD_TOGGLE_0 0x02000000 #define OHCI_TD_TOGGLE_1 0x03000000 #define OHCI_TD_TOGGLE_MASK 0x03000000 #define OHCI_TD_GET_EC(x) (((x) >> 26) & 3) /* Error Count */ #define OHCI_TD_GET_CC(x) ((x) >> 28) /* Condition Code */ #define OHCI_TD_NOCC 0xf0000000 volatile uint32_t td_cbp; /* Current Buffer Pointer */ volatile uint32_t td_next; /* Next TD */ volatile uint32_t td_be; /* Buffer End */ /* * Extra information needed: */ struct ohci_td *obj_next; uint32_t td_self; uint16_t len; } __aligned(OHCI_TD_ALIGN) ohci_td_t; typedef struct ohci_itd { volatile uint32_t itd_flags; #define OHCI_ITD_GET_SF(x) ((x) & 0x0000ffff) #define OHCI_ITD_SET_SF(x) ((x) & 0xffff) #define OHCI_ITD_GET_DI(x) (((x) >> 21) & 7) /* Delay Interrupt */ #define OHCI_ITD_SET_DI(x) ((x) << 21) #define OHCI_ITD_NOINTR 0x00e00000 #define OHCI_ITD_GET_FC(x) ((((x) >> 24) & 7)+1) /* Frame Count */ #define OHCI_ITD_SET_FC(x) (((x)-1) << 24) #define OHCI_ITD_GET_CC(x) ((x) >> 28) /* Condition Code */ #define OHCI_ITD_NOCC 0xf0000000 #define OHCI_ITD_NOFFSET 8 volatile uint32_t itd_bp0; /* Buffer Page 0 */ volatile uint32_t itd_next; /* Next ITD */ volatile uint32_t itd_be; /* Buffer End */ volatile uint16_t itd_offset[OHCI_ITD_NOFFSET]; /* Buffer offsets and Status */ #define OHCI_ITD_PAGE_SELECT 0x00001000 #define OHCI_ITD_MK_OFFS(len) (0xe000 | ((len) & 0x1fff)) #define OHCI_ITD_PSW_LENGTH(x) ((x) & 0xfff) /* Transfer length */ #define OHCI_ITD_PSW_GET_CC(x) ((x) >> 12) /* Condition Code */ /* * Extra information needed: */ struct ohci_itd *obj_next; uint32_t itd_self; uint8_t frames; } __aligned(OHCI_ITD_ALIGN) ohci_itd_t; #define OHCI_CC_NO_ERROR 0 #define OHCI_CC_CRC 1 #define OHCI_CC_BIT_STUFFING 2 #define OHCI_CC_DATA_TOGGLE_MISMATCH 3 #define OHCI_CC_STALL 4 #define OHCI_CC_DEVICE_NOT_RESPONDING 5 #define OHCI_CC_PID_CHECK_FAILURE 6 #define OHCI_CC_UNEXPECTED_PID 7 #define OHCI_CC_DATA_OVERRUN 8 #define OHCI_CC_DATA_UNDERRUN 9 #define OHCI_CC_BUFFER_OVERRUN 12 #define OHCI_CC_BUFFER_UNDERRUN 13 #define OHCI_CC_NOT_ACCESSED 15 /* Some delay needed when changing certain registers. */ #define OHCI_ENABLE_POWER_DELAY 5 #define OHCI_READ_DESC_DELAY 5 #define OHCI_NO_EDS (2*OHCI_NO_INTRS) struct ohci_hw_softc { struct ohci_hcca hcca; ohci_ed_t ctrl_start; ohci_ed_t bulk_start; ohci_ed_t isoc_start; ohci_ed_t intr_start[OHCI_NO_EDS]; }; typedef struct ohci_softc { struct ohci_hw_softc sc_hw; /* hardware structures first */ ohci_ed_t *sc_ctrl_p_last; ohci_ed_t *sc_bulk_p_last; ohci_ed_t *sc_isoc_p_last; ohci_ed_t *sc_intr_p_last[OHCI_NO_EDS]; uint16_t sc_intr_stat[OHCI_NO_EDS]; struct usbd_bus sc_bus; /* base device */ uint32_t sc_physaddr; bus_space_tag_t iot; bus_space_handle_t ioh; bus_size_t sc_size; void *ih; struct resource *io_res; struct resource *irq_res; uint32_t sc_eintrs; /* enabled interrupts */ uint8_t sc_noport; uint8_t sc_addr; /* device address */ uint8_t sc_conf; /* device configuration */ device_t sc_dev; struct usbd_xfer *sc_intrxfer; uint8_t sc_vendor[16]; int sc_id_vendor; #if defined(__NetBSD__) void *sc_powerhook; /* cookie from power hook */ void *sc_shutdownhook; /* cookie from shutdown hook */ #endif uint32_t sc_control; /* Preserved during suspend/standby */ uint32_t sc_intre; LIST_HEAD(, usbd_xfer) sc_interrupt_list_head; struct __callout sc_tmo_rhsc; } ohci_softc_t; usbd_status ohci_init(ohci_softc_t *sc); void ohci_detach(struct ohci_softc *sc); void ohci_suspend(ohci_softc_t *sc); void ohci_resume(ohci_softc_t *sc); void ohci_interrupt(ohci_softc_t *sc); #endif /* _OHCI_H_ */ pwcbsd/usb/ohci_pci.c000644 000423 000000 00000024425 10551670753 015330 0ustar00luigiwheel000000 000000 /* * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (augustss@carlstedt.se) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD: src/sys/dev/usb/ohci_pci.c,v 1.48 2006/09/03 00:27:42 jmg Exp $"); /* * USB Open Host Controller driver. * * OHCI spec: http://www.intel.com/design/usb/ohci11d.pdf */ /* The low level controller code for OHCI has been split into * PCI probes and OHCI specific code. This was done to facilitate the * sharing of code between *BSD's */ #include "opt_bus.h" #include #include #include #include #include /* LIST_XXX() */ #include #include #define INCLUDE_PCIXXX_H #include #include #include #include #define PCI_OHCI_VENDORID_ACERLABS 0x10b9 #define PCI_OHCI_VENDORID_AMD 0x1022 #define PCI_OHCI_VENDORID_APPLE 0x106b #define PCI_OHCI_VENDORID_ATI 0x1002 #define PCI_OHCI_VENDORID_CMDTECH 0x1095 #define PCI_OHCI_VENDORID_NEC 0x1033 #define PCI_OHCI_VENDORID_NVIDIA 0x12D2 #define PCI_OHCI_VENDORID_NVIDIA2 0x10DE #define PCI_OHCI_VENDORID_OPTI 0x1045 #define PCI_OHCI_VENDORID_SIS 0x1039 #define PCI_OHCI_VENDORID_SUN 0x108e #define PCI_OHCI_BASE_REG 0x10 static int ohci_pci_attach(device_t self); static int ohci_pci_detach(device_t self); static int ohci_pci_suspend(device_t self); static int ohci_pci_resume(device_t self); static int ohci_pci_suspend(device_t self) { ohci_softc_t *sc = device_get_softc(self); int err; err = bus_generic_suspend(self); if(err) { return err; } ohci_suspend(sc); return 0; } static int ohci_pci_resume(device_t self) { ohci_softc_t *sc = device_get_softc(self); u_int32_t reg, int_line; if(pci_get_powerstate(self) != PCI_POWERSTATE_D0) { device_printf(self, "chip is in D%d mode " "-- setting to D0\n", pci_get_powerstate(self)); reg = pci_read_config(self, PCI_CBMEM, 4); int_line = pci_read_config(self, PCIR_INTLINE, 4); pci_set_powerstate(self, PCI_POWERSTATE_D0); pci_write_config(self, PCI_CBMEM, reg, 4); pci_write_config(self, PCIR_INTLINE, int_line, 4); } ohci_resume(sc); bus_generic_resume(self); return 0; } static const char * ohci_pci_match(device_t self) { u_int32_t device_id = pci_get_devid(self); if(device_id == 0x523710b9) { return ("AcerLabs M5237 (Aladdin-V) USB controller"); } if(device_id == 0x740c1022) { return ("AMD-756 USB Controller"); } if(device_id == 0x74141022) { return ("AMD-766 USB Controller"); } if (device_id == 0x43741002) return "ATI SB400 USB Controller"; if (device_id == 0x43751002) return "ATI SB400 USB Controller"; if(device_id == 0x06701095) { return ("CMD Tech 670 (USB0670) USB controller"); } if(device_id == 0x06731095) { return ("CMD Tech 673 (USB0673) USB controller"); } if(device_id == 0xc8611045) { return ("OPTi 82C861 (FireLink) USB controller"); } if(device_id == 0x00351033) { return ("NEC uPD 9210 USB controller"); } if(device_id == 0x00d710de) { return ("nVidia nForce3 USB Controller"); } if(device_id == 0x70011039) { return ("SiS 5571 USB controller"); } if (device_id == 0x1103108e) return "Sun PCIO-2 USB controller"; if(device_id == 0x0019106b) { return ("Apple KeyLargo USB controller"); } if ((pci_get_class(self) == PCIC_SERIALBUS) && (pci_get_subclass(self) == PCIS_SERIALBUS_USB) && (pci_get_progif(self) == PCI_INTERFACE_OHCI)) { return ("OHCI (generic) USB controller"); } return NULL; } static int ohci_pci_probe(device_t self) { const char *desc = ohci_pci_match(self); if (desc) { device_set_desc(self, desc); return 0; } else { return ENXIO; } } static int ohci_pci_attach(device_t self) { ohci_softc_t *sc; int rid; int err; sc = usbd_mem_alloc(device_get_dma_tag(self), sizeof(*sc), LOG2(OHCI_HCCA_ALIGN)); if(sc == NULL) { device_printf(self, "Could not allocate sc\n"); return ENXIO; } #if 1 bzero(sc, sizeof(*sc)); #endif sc->sc_physaddr = usbd_mem_vtophys(sc, sizeof(*sc)); /* physical address of sc */ mtx_init(&sc->sc_bus.mtx, "usb lock", NULL, MTX_DEF|MTX_RECURSE); device_set_softc(self, sc); sc->sc_dev = self; pci_enable_busmaster(self); sc->sc_bus.dma_tag = usbd_dma_tag_alloc(device_get_dma_tag(self), USB_PAGE_SIZE, USB_PAGE_SIZE); if (sc->sc_bus.dma_tag == NULL) { device_printf(self, "Could not allocate DMA tag\n"); goto error; } rid = PCI_CBMEM; sc->io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid, RF_ACTIVE); if(!sc->io_res) { device_printf(self, "Could not map memory\n"); goto error; } sc->iot = rman_get_bustag(sc->io_res); sc->ioh = rman_get_bushandle(sc->io_res); rid = 0; sc->irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); if(sc->irq_res == NULL) { device_printf(self, "Could not allocate irq\n"); goto error; } sc->sc_bus.bdev = device_add_child(self, "usb", -1); if(!sc->sc_bus.bdev) { device_printf(self, "Could not add USB device\n"); goto error; } device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); device_set_softc(sc->sc_bus.bdev, &sc->sc_bus); /* ohci_pci_match will never return NULL if ohci_pci_probe succeeded */ device_set_desc(sc->sc_bus.bdev, ohci_pci_match(self)); switch (pci_get_vendor(self)) { case PCI_OHCI_VENDORID_ACERLABS: sprintf(sc->sc_vendor, "AcerLabs"); break; case PCI_OHCI_VENDORID_AMD: sprintf(sc->sc_vendor, "AMD"); break; case PCI_OHCI_VENDORID_APPLE: sprintf(sc->sc_vendor, "Apple"); break; case PCI_OHCI_VENDORID_ATI: sprintf(sc->sc_vendor, "ATI"); break; case PCI_OHCI_VENDORID_CMDTECH: sprintf(sc->sc_vendor, "CMDTECH"); break; case PCI_OHCI_VENDORID_NEC: sprintf(sc->sc_vendor, "NEC"); break; case PCI_OHCI_VENDORID_NVIDIA: case PCI_OHCI_VENDORID_NVIDIA2: sprintf(sc->sc_vendor, "nVidia"); break; case PCI_OHCI_VENDORID_OPTI: sprintf(sc->sc_vendor, "OPTi"); break; case PCI_OHCI_VENDORID_SIS: sprintf(sc->sc_vendor, "SiS"); break; case PCI_OHCI_VENDORID_SUN: sprintf(sc->sc_vendor, "SUN"); break; default: if(bootverbose) { device_printf(self, "(New OHCI DeviceId=0x%08x)\n", pci_get_devid(self)); } sprintf(sc->sc_vendor, "(0x%04x)", pci_get_vendor(self)); } /* sc->sc_bus.usbrev; set by ohci_init() */ err = bus_setup_intr(self, sc->irq_res, INTR_TYPE_BIO|INTR_MPSAFE, (void *)(void *)ohci_interrupt, sc, &sc->ih); if(err) { device_printf(self, "Could not setup irq, %d\n", err); sc->ih = NULL; goto error; } err = ohci_init(sc); if(!err) { err = device_probe_and_attach(sc->sc_bus.bdev); } if(err) { device_printf(self, "USB init failed\n"); goto error; } return 0; error: ohci_pci_detach(self); return ENXIO; } static int ohci_pci_detach(device_t self) { ohci_softc_t *sc = device_get_softc(self); if(sc->sc_bus.bdev) { device_delete_child(self, sc->sc_bus.bdev); sc->sc_bus.bdev = NULL; } pci_disable_busmaster(self); if(sc->irq_res && sc->ih) { /* only call ohci_detach() * after ohci_init() */ ohci_detach(sc); int err = bus_teardown_intr(self, sc->irq_res, sc->ih); if(err) { /* XXX or should we panic? */ device_printf(self, "Could not tear down irq, %d\n", err); } sc->ih = NULL; } if(sc->irq_res) { bus_release_resource(self, SYS_RES_IRQ, 0, sc->irq_res); sc->irq_res = NULL; } if(sc->io_res) { bus_release_resource(self, SYS_RES_MEMORY, PCI_CBMEM, sc->io_res); sc->io_res = NULL; } if(sc->sc_bus.dma_tag) { usbd_dma_tag_free(sc->sc_bus.dma_tag); } mtx_destroy(&sc->sc_bus.mtx); usbd_mem_free(sc, sizeof(*sc)); device_set_softc(self, NULL); return 0; } static driver_t ohci_driver = { .name = "ohci", .methods = (device_method_t []) { /* device interface */ DEVMETHOD(device_probe, ohci_pci_probe), DEVMETHOD(device_attach, ohci_pci_attach), DEVMETHOD(device_detach, ohci_pci_detach), DEVMETHOD(device_suspend, ohci_pci_suspend), DEVMETHOD(device_resume, ohci_pci_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), /* bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), {0, 0} }, .size = 0, }; static devclass_t ohci_devclass; DRIVER_MODULE(ohci, pci, ohci_driver, ohci_devclass, 0, 0); DRIVER_MODULE(ohci, cardbus, ohci_driver, ohci_devclass, 0, 0); pwcbsd/usb/rio500_usb.h000644 000423 000000 00000003210 10551670753 015434 0ustar00luigiwheel000000 000000 /*- ---------------------------------------------------------------------- Copyright (C) 2000 Cesar Miquel (miquel@df.uba.ar) Redistribution and use in source and binary forms, with or without modification, are permitted under any licence of your choise which meets the open source licence definiton http://www.opensource.org/opd.html such as the GNU licence or the BSD licence. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License or the BSD license for more details. ---------------------------------------------------------------------- Modified for FreeBSD by Iwasa Kazmi ---------------------------------------------------------------------- */ /* $FreeBSD: src/sys/dev/usb/rio500_usb.h,v 1.2 2005/01/06 01:43:28 imp Exp $ */ #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) #include #endif struct RioCommand { #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) uint16_t length; #else short length; #endif int request; int requesttype; int value; int index; void *buffer; int timeout; }; #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) #define RIO_SEND_COMMAND _IOWR('U', 200, struct RioCommand) #define RIO_RECV_COMMAND _IOWR('U', 201, struct RioCommand) #else #define RIO_SEND_COMMAND 0x1 #define RIO_RECV_COMMAND 0x2 #endif #define RIO_DIR_OUT 0x0 #define RIO_DIR_IN 0x1 pwcbsd/usb/uark.c000644 000423 000000 00000000000 10551670753 014474 0ustar00luigiwheel000000 000000 pwcbsd/usb/ubsa.c000644 000423 000000 00000063244 10551670753 014507 0ustar00luigiwheel000000 000000 /*- * Copyright (c) 2002, Alexander Kabaev . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD: src/sys/dev/usb/ubsa.c,v 1.19 2006/09/07 00:06:41 imp Exp $"); /*- * Copyright (c) 2001 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Ichiro FUKUHARA (ichiro@ichiro.org). * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" #ifdef USB_DEBUG #define DPRINTF(n,fmt,...) \ do { if (ubsa_debug > (n)) { \ printf("%s: " fmt, __FUNCTION__,## __VA_ARGS__); } } while (0) static int ubsa_debug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, ubsa, CTLFLAG_RW, 0, "USB ubsa"); SYSCTL_INT(_hw_usb_ubsa, OID_AUTO, debug, CTLFLAG_RW, &ubsa_debug, 0, "ubsa debug level"); #else #define DPRINTF(...) #endif #define UBSA_MODVER 1 /* module version */ #define UBSA_N_TRANSFER 14 /* units */ #define UBSA_BSIZE 64 /* bytes */ #define UBSA_CONFIG_INDEX 1 #define UBSA_IFACE_INDEX 0 enum { UBSA_REG_BAUDRATE, UBSA_REG_STOP_BITS, UBSA_REG_DATA_BITS, UBSA_REG_PARITY, UBSA_REG_DTR, UBSA_REG_RTS, UBSA_REG_BREAK, UBSA_REG_FLOW_CTRL, USBA_N_REG }; #define UBSA_PARITY_NONE 0x00 #define UBSA_PARITY_EVEN 0x01 #define UBSA_PARITY_ODD 0x02 #define UBSA_PARITY_MARK 0x03 #define UBSA_PARITY_SPACE 0x04 #define UBSA_FLOW_NONE 0x0000 #define UBSA_FLOW_OCTS 0x0001 #define UBSA_FLOW_ODSR 0x0002 #define UBSA_FLOW_IDSR 0x0004 #define UBSA_FLOW_IDTR 0x0008 #define UBSA_FLOW_IRTS 0x0010 #define UBSA_FLOW_ORTS 0x0020 #define UBSA_FLOW_UNKNOWN 0x0040 #define UBSA_FLOW_OXON 0x0080 #define UBSA_FLOW_IXON 0x0100 /* line status register */ #define UBSA_LSR_TSRE 0x40 /* Transmitter empty: byte sent */ #define UBSA_LSR_TXRDY 0x20 /* Transmitter buffer empty */ #define UBSA_LSR_BI 0x10 /* Break detected */ #define UBSA_LSR_FE 0x08 /* Framing error: bad stop bit */ #define UBSA_LSR_PE 0x04 /* Parity error */ #define UBSA_LSR_OE 0x02 /* Overrun, lost incoming byte */ #define UBSA_LSR_RXRDY 0x01 /* Byte ready in Receive Buffer */ #define UBSA_LSR_RCV_MASK 0x1f /* Mask for incoming data or error */ /* modem status register */ /* All deltas are from the last read of the MSR. */ #define UBSA_MSR_DCD 0x80 /* Current Data Carrier Detect */ #define UBSA_MSR_RI 0x40 /* Current Ring Indicator */ #define UBSA_MSR_DSR 0x20 /* Current Data Set Ready */ #define UBSA_MSR_CTS 0x10 /* Current Clear to Send */ #define UBSA_MSR_DDCD 0x08 /* DCD has changed state */ #define UBSA_MSR_TERI 0x04 /* RI has toggled low to high */ #define UBSA_MSR_DDSR 0x02 /* DSR has changed state */ #define UBSA_MSR_DCTS 0x01 /* CTS has changed state */ struct ubsa_softc { struct ucom_softc sc_ucom; struct usbd_xfer * sc_xfer[UBSA_N_TRANSFER]; u_int16_t sc_flag; #define UBSA_FLAG_WRITE_STALL 0x0001 #define UBSA_FLAG_READ_STALL 0x0002 #define UBSA_FLAG_INTR_STALL 0x0004 u_int16_t sc_reg_flag; u_int16_t sc_reg[USBA_N_REG]; u_int8_t sc_iface_no; /* interface number */ u_int8_t sc_iface_index; /* interface index */ u_int8_t sc_dtr; /* current DTR state */ u_int8_t sc_rts; /* current RTS state */ u_int8_t sc_lsr; /* local status register */ u_int8_t sc_msr; /* UBSA status register */ }; static device_probe_t ubsa_probe; static device_attach_t ubsa_attach; static device_detach_t ubsa_detach; static void ubsa_request(struct ubsa_softc *sc, u_int8_t index, u_int16_t value); static void ubsa_set_dtr(struct ucom_softc *ucom, u_int8_t onoff); static void ubsa_set_rts(struct ucom_softc *ucom, u_int8_t onoff); static void ubsa_set_break(struct ucom_softc *ucom, u_int8_t onoff); static u_int8_t ubsa_baudrate(struct ubsa_softc *sc, speed_t speed); static void ubsa_parity(struct ubsa_softc *sc, tcflag_t cflag); static void ubsa_databits(struct ubsa_softc *sc, tcflag_t cflag); static void ubsa_stopbits(struct ubsa_softc *sc, tcflag_t cflag); static void ubsa_flow(struct ubsa_softc *sc, tcflag_t cflag, tcflag_t iflag); static int ubsa_param(struct ucom_softc *ucom, struct termios *ti); static int ubsa_open(struct ucom_softc *ucom); static void ubsa_close(struct ucom_softc *ucom); static void ubsa_start_read(struct ucom_softc *ucom); static void ubsa_stop_read(struct ucom_softc *ucom); static void ubsa_start_write(struct ucom_softc *ucom); static void ubsa_stop_write(struct ucom_softc *ucom); static void ubsa_get_status(struct ucom_softc *ucom, u_int8_t *lsr, u_int8_t *msr); static void ubsa_write_callback(struct usbd_xfer *xfer); static void ubsa_write_clear_stall_callback(struct usbd_xfer *xfer); static void ubsa_read_callback(struct usbd_xfer *xfer); static void ubsa_read_clear_stall_callback(struct usbd_xfer *xfer); static void ubsa_intr_callback(struct usbd_xfer *xfer); static void ubsa_intr_clear_stall_callback(struct usbd_xfer *xfer); static void ubsa_request_callback(struct usbd_xfer *xfer, u_int8_t index); static void ubsa_set_baudrate_callback(struct usbd_xfer *xfer); static void ubsa_set_stop_bits_callback(struct usbd_xfer *xfer); static void ubsa_set_data_bits_callback(struct usbd_xfer *xfer); static void ubsa_set_parity_callback(struct usbd_xfer *xfer); static void ubsa_set_dtr_callback(struct usbd_xfer *xfer); static void ubsa_set_rts_callback(struct usbd_xfer *xfer); static void ubsa_set_break_callback(struct usbd_xfer *xfer); static void ubsa_set_flow_ctrl_callback(struct usbd_xfer *xfer); static const struct usbd_config ubsa_config[UBSA_N_TRANSFER] = { [0] = { .type = UE_BULK, .endpoint = -1, /* any */ .direction = UE_DIR_OUT, .bufsize = UBSA_BSIZE, /* bytes */ .flags = 0, .callback = &ubsa_write_callback, }, [1] = { .type = UE_BULK, .endpoint = -1, /* any */ .direction = UE_DIR_IN, .bufsize = UBSA_BSIZE, /* bytes */ .flags = USBD_SHORT_XFER_OK, .callback = &ubsa_read_callback, }, [2] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .callback = &ubsa_write_clear_stall_callback, .timeout = 1000, /* 1 second */ }, [3] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .callback = &ubsa_read_clear_stall_callback, .timeout = 1000, /* 1 second */ }, [4] = { .type = UE_INTERRUPT, .endpoint = -1, /* any */ .direction = UE_DIR_IN, .flags = USBD_SHORT_XFER_OK, .bufsize = 0, /* use wMaxPacketSize */ .callback = &ubsa_intr_callback, }, [5] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .callback = &ubsa_intr_clear_stall_callback, .timeout = 1000, /* 1 second */ }, [6+UBSA_REG_BAUDRATE] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .callback = &ubsa_set_baudrate_callback, .timeout = 1000, /* 1 second */ }, [6+UBSA_REG_STOP_BITS] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .callback = &ubsa_set_stop_bits_callback, .timeout = 1000, /* 1 second */ }, [6+UBSA_REG_DATA_BITS] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .callback = &ubsa_set_data_bits_callback, .timeout = 1000, /* 1 second */ }, [6+UBSA_REG_PARITY] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .callback = &ubsa_set_parity_callback, .timeout = 1000, /* 1 second */ }, [6+UBSA_REG_DTR] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .callback = &ubsa_set_dtr_callback, .timeout = 1000, /* 1 second */ }, [6+UBSA_REG_RTS] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .callback = &ubsa_set_rts_callback, .timeout = 1000, /* 1 second */ }, [6+UBSA_REG_BREAK] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .callback = &ubsa_set_break_callback, .timeout = 1000, /* 1 second */ }, [6+UBSA_REG_FLOW_CTRL] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .callback = &ubsa_set_flow_ctrl_callback, .timeout = 1000, /* 1 second */ }, }; static const struct ucom_callback ubsa_callback = { .ucom_get_status = &ubsa_get_status, .ucom_set_dtr = &ubsa_set_dtr, .ucom_set_rts = &ubsa_set_rts, .ucom_set_break = &ubsa_set_break, .ucom_param = &ubsa_param, .ucom_open = &ubsa_open, .ucom_close = &ubsa_close, .ucom_start_read = &ubsa_start_read, .ucom_stop_read = &ubsa_stop_read, .ucom_start_write = &ubsa_start_write, .ucom_stop_write = &ubsa_stop_write, }; static const struct ubsa_product { u_int16_t vendor; u_int16_t product; } ubsa_products [] = { /* BELKIN F5U103 */ { USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5U103 }, /* BELKIN F5U120 */ { USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5U120 }, /* GoHubs GO-COM232 */ { USB_VENDOR_ETEK, USB_PRODUCT_ETEK_1COM }, /* GoHubs GO-COM232 */ { USB_VENDOR_GOHUBS, USB_PRODUCT_GOHUBS_GOCOM232 }, /* Peracom */ { USB_VENDOR_PERACOM, USB_PRODUCT_PERACOM_SERIAL1 }, /* Vodafone */ { USB_VENDOR_VODAFONE, USB_PRODUCT_VODAFONE_MC3G }, { 0, 0 } }; static device_method_t ubsa_methods[] = { DEVMETHOD(device_probe, ubsa_probe), DEVMETHOD(device_attach, ubsa_attach), DEVMETHOD(device_detach, ubsa_detach), { 0, 0 } }; static devclass_t ubsa_devclass; static driver_t ubsa_driver = { .name = "ubsa", .methods = ubsa_methods, .size = sizeof(struct ubsa_softc), }; DRIVER_MODULE(ubsa, uhub, ubsa_driver, ubsa_devclass, usbd_driver_load, 0); MODULE_DEPEND(ubsa, usb, 1, 1, 1); MODULE_DEPEND(ubsa, ucom, UCOM_MINVER, UCOM_PREFVER, UCOM_MAXVER); MODULE_VERSION(ubsa, UBSA_MODVER); static int ubsa_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); const struct ubsa_product *up = ubsa_products; if (uaa->iface) { return UMATCH_NONE; } while (up->vendor) { if ((up->vendor == uaa->vendor) && (up->product == uaa->product)) { return UMATCH_VENDOR_PRODUCT; } up++; } return UMATCH_NONE; } static int ubsa_attach(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct ubsa_softc *sc = device_get_softc(dev); struct usbd_interface *iface; usb_interface_descriptor_t *id; int error; DPRINTF(0, "sc=%p\n", sc); if (sc == NULL) { return ENOMEM; } usbd_set_desc(dev, uaa->device); /* configure the device */ error = usbd_set_config_index(uaa->device, UBSA_CONFIG_INDEX, 1); if (error) { DPRINTF(0, "failed to set configuration, error=%s\n", usbd_errstr(error)); goto detach; } iface = usbd_get_iface(uaa->device, UBSA_IFACE_INDEX); if (iface == NULL) { DPRINTF(0, "no interface\n"); goto detach; } id = usbd_get_interface_descriptor(iface); if (id == NULL) { DPRINTF(0, "no interface descriptor\n"); goto detach; } sc->sc_iface_no = id->bInterfaceNumber; sc->sc_iface_index = UBSA_IFACE_INDEX; error = usbd_transfer_setup(uaa->device, sc->sc_iface_index, sc->sc_xfer, ubsa_config, UBSA_N_TRANSFER, sc, &Giant); if (error) { DPRINTF(0, "could not allocate all pipes\n"); goto detach; } sc->sc_dtr = -1; sc->sc_rts = -1; error = ucom_attach(&(sc->sc_ucom), 1, sc, &ubsa_callback, &Giant); if (error) { DPRINTF(0, "ucom_attach failed\n"); goto detach; } return 0; detach: ubsa_detach(dev); return ENXIO; } static int ubsa_detach(device_t dev) { struct ubsa_softc *sc = device_get_softc(dev); DPRINTF(0, "sc=%p\n", sc); ucom_detach(&(sc->sc_ucom), 1); usbd_transfer_unsetup(sc->sc_xfer, UBSA_N_TRANSFER); return 0; } static void ubsa_request(struct ubsa_softc *sc, u_int8_t index, u_int16_t value) { if (index >= USBA_N_REG) { panic("invalid register index!"); } sc->sc_reg_flag |= (1 << index); sc->sc_reg[index] = value; usbd_transfer_start(sc->sc_xfer[6+index]); return; } static void ubsa_set_dtr(struct ucom_softc *ucom, u_int8_t onoff) { struct ubsa_softc *sc = ucom->sc_parent; DPRINTF(0, "onoff = %d\n", onoff); if (sc->sc_dtr != onoff) { sc->sc_dtr = onoff; ubsa_request(sc, UBSA_REG_DTR, onoff ? 1 : 0); } return; } static void ubsa_set_rts(struct ucom_softc *ucom, u_int8_t onoff) { struct ubsa_softc *sc = ucom->sc_parent; DPRINTF(0, "onoff = %d\n", onoff); if (sc->sc_rts != onoff) { sc->sc_rts = onoff; ubsa_request(sc, UBSA_REG_RTS, onoff ? 1 : 0); } return; } static void ubsa_set_break(struct ucom_softc *ucom, u_int8_t onoff) { struct ubsa_softc *sc = ucom->sc_parent; DPRINTF(0, "onoff = %d\n", onoff); ubsa_request(sc, UBSA_REG_BREAK, onoff ? 1 : 0); return; } static u_int8_t ubsa_baudrate(struct ubsa_softc *sc, speed_t speed) { u_int16_t value = 0; DPRINTF(0, "speed = %d\n", speed); switch(speed) { case B0: break; case B300: case B600: case B1200: case B2400: case B4800: case B9600: case B19200: case B38400: case B57600: case B115200: case B230400: value = B230400 / speed; break; default: DPRINTF(0, "unsupported baudrate\n"); return 1; }; if (speed == B0) { ubsa_flow(sc, 0, 0); ubsa_set_dtr(&(sc->sc_ucom), 0); ubsa_set_rts(&(sc->sc_ucom), 0); } else { ubsa_request(sc, UBSA_REG_BAUDRATE, value); } return 0; } static void ubsa_parity(struct ubsa_softc *sc, tcflag_t cflag) { u_int16_t value; DPRINTF(0, "flag = 0x%x\n", cflag); if (cflag & PARENB) value = (cflag & PARODD) ? UBSA_PARITY_ODD : UBSA_PARITY_EVEN; else value = UBSA_PARITY_NONE; ubsa_request(sc, UBSA_REG_PARITY, value); return; } static void ubsa_databits(struct ubsa_softc *sc, tcflag_t cflag) { u_int16_t value; DPRINTF(0, "cflag = 0x%x\n", cflag); switch (cflag & CSIZE) { case CS5: value = 0; break; case CS6: value = 1; break; case CS7: value = 2; break; case CS8: value = 3; break; default: DPRINTF(0, "unsupported databits requested, " "forcing default of 8\n"); value = 3; } ubsa_request(sc, UBSA_REG_DATA_BITS, value); return; } static void ubsa_stopbits(struct ubsa_softc *sc, tcflag_t cflag) { u_int16_t value; DPRINTF(0, "cflag = 0x%x\n", cflag); value = (cflag & CSTOPB) ? 1 : 0; ubsa_request(sc, UBSA_REG_STOP_BITS, value); return; } static void ubsa_flow(struct ubsa_softc *sc, tcflag_t cflag, tcflag_t iflag) { u_int16_t value; DPRINTF(0, "cflag = 0x%x, iflag = 0x%x\n", cflag, iflag); value = 0; if (cflag & CRTSCTS) value |= UBSA_FLOW_OCTS | UBSA_FLOW_IRTS; if (iflag & (IXON|IXOFF)) value |= UBSA_FLOW_OXON | UBSA_FLOW_IXON; ubsa_request(sc, UBSA_REG_FLOW_CTRL, value); return; } static int ubsa_param(struct ucom_softc *ucom, struct termios *ti) { struct ubsa_softc *sc = ucom->sc_parent; DPRINTF(0, "sc = %p\n", sc); if (ubsa_baudrate(sc, ti->c_ospeed)) { return EIO; } ubsa_parity(sc, ti->c_cflag); ubsa_databits(sc, ti->c_cflag); ubsa_stopbits(sc, ti->c_cflag); ubsa_flow(sc, ti->c_cflag, ti->c_iflag); return (0); } static int ubsa_open(struct ucom_softc *ucom) { struct ubsa_softc *sc = ucom->sc_parent; /* clear stall first: */ sc->sc_flag |= (UBSA_FLAG_WRITE_STALL| UBSA_FLAG_READ_STALL); usbd_transfer_start(sc->sc_xfer[4]); return 0; } static void ubsa_close(struct ucom_softc *ucom) { struct ubsa_softc *sc = ucom->sc_parent; usbd_transfer_stop(sc->sc_xfer[5]); usbd_transfer_stop(sc->sc_xfer[4]); return; } static void ubsa_start_read(struct ucom_softc *ucom) { struct ubsa_softc *sc = ucom->sc_parent; usbd_transfer_start(sc->sc_xfer[1]); return; } static void ubsa_stop_read(struct ucom_softc *ucom) { struct ubsa_softc *sc = ucom->sc_parent; usbd_transfer_stop(sc->sc_xfer[3]); usbd_transfer_stop(sc->sc_xfer[1]); return; } static void ubsa_start_write(struct ucom_softc *ucom) { struct ubsa_softc *sc = ucom->sc_parent; usbd_transfer_start(sc->sc_xfer[0]); return; } static void ubsa_stop_write(struct ucom_softc *ucom) { struct ubsa_softc *sc = ucom->sc_parent; usbd_transfer_stop(sc->sc_xfer[2]); usbd_transfer_stop(sc->sc_xfer[0]); return; } static void ubsa_get_status(struct ucom_softc *ucom, u_int8_t *lsr, u_int8_t *msr) { struct ubsa_softc *sc = ucom->sc_parent; DPRINTF(0, "\n"); if (lsr) { *lsr = sc->sc_lsr; } if (msr) { *msr = sc->sc_msr; } return; } static void ubsa_write_callback(struct usbd_xfer *xfer) { struct ubsa_softc *sc = xfer->priv_sc; u_int32_t actlen; USBD_CHECK_STATUS(xfer); tr_error: if (xfer->error != USBD_CANCELLED) { sc->sc_flag |= UBSA_FLAG_WRITE_STALL; usbd_transfer_start(sc->sc_xfer[2]); } return; tr_setup: tr_transferred: if (sc->sc_flag & UBSA_FLAG_WRITE_STALL) { usbd_transfer_start(sc->sc_xfer[2]); return; } if(ucom_get_data(&(sc->sc_ucom), xfer->buffer, UBSA_BSIZE, &actlen)) { xfer->length = actlen; usbd_start_hardware(xfer); } return; } static void ubsa_write_clear_stall_callback(struct usbd_xfer *xfer) { struct ubsa_softc *sc = xfer->priv_sc; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, sc->sc_xfer[0]); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, sc->sc_xfer[0]); sc->sc_flag &= ~UBSA_FLAG_WRITE_STALL; usbd_transfer_start(sc->sc_xfer[0]); return; tr_error: sc->sc_flag &= ~UBSA_FLAG_WRITE_STALL; DPRINTF(0, "clear stall failed, error=%s\n", usbd_errstr(xfer->error)); return; } static void ubsa_read_callback(struct usbd_xfer *xfer) { struct ubsa_softc *sc = xfer->priv_sc; USBD_CHECK_STATUS(xfer); tr_error: if (xfer->error != USBD_CANCELLED) { sc->sc_flag |= UBSA_FLAG_READ_STALL; usbd_transfer_start(sc->sc_xfer[3]); } return; tr_transferred: ucom_put_data(&(sc->sc_ucom), xfer->buffer, xfer->actlen); tr_setup: if (sc->sc_flag & UBSA_FLAG_READ_STALL) { usbd_transfer_start(sc->sc_xfer[3]); } else { usbd_start_hardware(xfer); } return; } static void ubsa_read_clear_stall_callback(struct usbd_xfer *xfer) { struct ubsa_softc *sc = xfer->priv_sc; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, sc->sc_xfer[1]); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, sc->sc_xfer[1]); sc->sc_flag &= ~UBSA_FLAG_READ_STALL; usbd_transfer_start(sc->sc_xfer[1]); return; tr_error: sc->sc_flag &= ~UBSA_FLAG_READ_STALL; DPRINTF(0, "clear stall failed, error=%s\n", usbd_errstr(xfer->error)); return; } static void ubsa_intr_callback(struct usbd_xfer *xfer) { struct ubsa_softc *sc = xfer->priv_sc; u_int8_t *buf = xfer->buffer; USBD_CHECK_STATUS(xfer); tr_error: if (xfer->error != USBD_CANCELLED) { sc->sc_flag |= UBSA_FLAG_INTR_STALL; usbd_transfer_start(sc->sc_xfer[5]); } return; tr_transferred: if (xfer->actlen >= 4) { /* incidentally, Belkin adapter status bits match UART 16550 bits */ sc->sc_lsr = buf[2]; sc->sc_msr = buf[3]; DPRINTF(0, "lsr = 0x%02x, msr = 0x%02x\n", sc->sc_lsr, sc->sc_msr); ucom_status_change(&(sc->sc_ucom)); } else { DPRINTF(0, "ignoring short packet, %d bytes\n", xfer->actlen); } tr_setup: if (sc->sc_flag & UBSA_FLAG_INTR_STALL) { usbd_transfer_start(sc->sc_xfer[5]); } else { usbd_start_hardware(xfer); } return; } static void ubsa_intr_clear_stall_callback(struct usbd_xfer *xfer) { struct ubsa_softc *sc = xfer->priv_sc; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, sc->sc_xfer[4]); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, sc->sc_xfer[4]); sc->sc_flag &= ~UBSA_FLAG_INTR_STALL; usbd_transfer_start(sc->sc_xfer[4]); return; tr_error: sc->sc_flag &= ~UBSA_FLAG_INTR_STALL; DPRINTF(0, "clear stall failed, error=%s\n", usbd_errstr(xfer->error)); return; } static void ubsa_request_callback(struct usbd_xfer *xfer, u_int8_t index) { static const u_int8_t mapping[USBA_N_REG] = { [UBSA_REG_BAUDRATE] = 0x00, [UBSA_REG_STOP_BITS] = 0x01, [UBSA_REG_DATA_BITS] = 0x02, [UBSA_REG_PARITY] = 0x03, [UBSA_REG_DTR] = 0x0A, [UBSA_REG_RTS] = 0x0B, [UBSA_REG_BREAK] = 0x0C, [UBSA_REG_FLOW_CTRL] = 0x10, }; usb_device_request_t *req = xfer->buffer; struct ubsa_softc *sc = xfer->priv_sc; u_int8_t request = mapping[index]; u_int16_t value = sc->sc_reg[index]; u_int16_t bitmask = (1 << index); USBD_CHECK_STATUS(xfer); tr_error: DPRINTF(0, "transfer failed, " "error=%s\n", usbd_errstr(xfer->error)); tr_setup: tr_transferred: if (sc->sc_reg_flag & bitmask) { sc->sc_reg_flag &= ~bitmask; req->bmRequestType = UT_WRITE_VENDOR_DEVICE; req->bRequest = request; USETW(req->wValue, value); USETW(req->wIndex, sc->sc_iface_no); USETW(req->wLength, 0); usbd_start_hardware(xfer); } return; } static void ubsa_set_baudrate_callback(struct usbd_xfer *xfer) { ubsa_request_callback(xfer, UBSA_REG_BAUDRATE); return; } static void ubsa_set_stop_bits_callback(struct usbd_xfer *xfer) { ubsa_request_callback(xfer, UBSA_REG_STOP_BITS); return; } static void ubsa_set_data_bits_callback(struct usbd_xfer *xfer) { ubsa_request_callback(xfer, UBSA_REG_DATA_BITS); return; } static void ubsa_set_parity_callback(struct usbd_xfer *xfer) { ubsa_request_callback(xfer, UBSA_REG_PARITY); return; } static void ubsa_set_dtr_callback(struct usbd_xfer *xfer) { ubsa_request_callback(xfer, UBSA_REG_DTR); return; } static void ubsa_set_rts_callback(struct usbd_xfer *xfer) { ubsa_request_callback(xfer, UBSA_REG_RTS); return; } static void ubsa_set_break_callback(struct usbd_xfer *xfer) { ubsa_request_callback(xfer, UBSA_REG_BREAK); return; } static void ubsa_set_flow_ctrl_callback(struct usbd_xfer *xfer) { ubsa_request_callback(xfer, UBSA_REG_FLOW_CTRL); return; } pwcbsd/usb/ubser.c000644 000423 000000 00000041203 10551670754 014665 0ustar00luigiwheel000000 000000 /*- * Copyright (c) 2004 Bernd Walter * * $URL: https://devel.bwct.de/svn/projects/ubser/ubser.c $ * $Date: 2007/01/12 11:33:00 $ * $Author: luigi $ * $Rev: 1127 $ */ /*- * Copyright (c) 2001-2002, Shunsuke Akiyama . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /*- * Copyright (c) 2000 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net). * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD: src/sys/dev/usb/ubser.c,v 1.20 2006/09/07 00:06:41 imp Exp $"); /* * BWCT serial adapter driver */ #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" #define UBSER_UNIT_MAX 32 /* Vendor Interface Requests */ #define VENDOR_GET_NUMSER 0x01 #define VENDOR_SET_BREAK 0x02 #define VENDOR_CLEAR_BREAK 0x03 #ifdef USB_DEBUG #define DPRINTF(sc,n,fmt,...) \ do { if (ubser_debug > (n)) { \ printf("%s:%s: " fmt, (sc)->sc_name, \ __FUNCTION__,## __VA_ARGS__); } } while (0) static int ubser_debug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, ubser, CTLFLAG_RW, 0, "USB ubser"); SYSCTL_INT(_hw_usb_ubser, OID_AUTO, debug, CTLFLAG_RW, &ubser_debug, 0, "ubser debug level"); #else #define DPRINTF(...) #endif #define UBSER_TR_DT_WRITE 0 #define UBSER_TR_DT_READ 1 #define UBSER_TR_CS_WRITE 2 #define UBSER_TR_CS_READ 3 #define UBSER_TR_DT_BREAK 4 #define UBSER_TR_MAX 5 struct ubser_softc { struct ucom_softc sc_ucom[UBSER_UNIT_MAX]; struct usbd_xfer *sc_xfer[UBSER_TR_MAX]; uint16_t sc_tx_size; uint8_t sc_numser; uint8_t sc_send_break[(UBSER_UNIT_MAX+7)/8]; uint8_t sc_flags; #define UBSER_FLAG_READ_STALL 0x01 #define UBSER_FLAG_WRITE_STALL 0x02 uint8_t sc_iface_no; uint8_t sc_iface_index; uint8_t sc_curr_tx_unit; uint8_t sc_curr_break_unit; uint8_t sc_name[16]; }; static device_probe_t ubser_probe; static device_attach_t ubser_attach; static device_detach_t ubser_detach; static int ubser_param(struct ucom_softc *ucom, struct termios *t); static void ubser_write_clear_stall_callback(struct usbd_xfer *xfer); static void ubser_write_callback(struct usbd_xfer *xfer); static void ubser_read_clear_stall_callback(struct usbd_xfer *xfer); static void ubser_read_callback(struct usbd_xfer *xfer); static void ubser_set_break(struct ucom_softc *ucom, u_int8_t onoff); static void ubser_send_break_callback(struct usbd_xfer *xfer); static int ubser_open(struct ucom_softc *ucom); static void ubser_start_write(struct ucom_softc *ucom); static const struct usbd_config ubser_config[UBSER_TR_MAX] = { [UBSER_TR_DT_WRITE] = { .type = UE_BULK, .endpoint = -1, /* any */ .direction = UE_DIR_OUT, .bufsize = 0, /* use wMaxPacketSize */ .flags = USBD_FORCE_SHORT_XFER, .callback = &ubser_write_callback, }, [UBSER_TR_DT_READ] = { .type = UE_BULK, .endpoint = -1, /* any */ .direction = UE_DIR_IN, .bufsize = 0, /* use wMaxPacketSize */ .flags = USBD_SHORT_XFER_OK, .callback = &ubser_read_callback, }, [UBSER_TR_CS_WRITE] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .flags = USBD_USE_DMA, .callback = &ubser_write_clear_stall_callback, .timeout = 1000, /* 1 second */ }, [UBSER_TR_CS_READ] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .flags = USBD_USE_DMA, .callback = &ubser_read_clear_stall_callback, .timeout = 1000, /* 1 second */ }, [UBSER_TR_DT_BREAK] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .flags = 0, .callback = &ubser_send_break_callback, .timeout = 1000, /* 1 second */ }, }; static const struct ucom_callback ubser_callback = { .ucom_open = &ubser_open, .ucom_set_break = &ubser_set_break, .ucom_param = &ubser_param, .ucom_start_write = &ubser_start_write, }; static device_method_t ubser_methods[] = { DEVMETHOD(device_probe, ubser_probe), DEVMETHOD(device_attach, ubser_attach), DEVMETHOD(device_detach, ubser_detach), { 0, 0 } }; static devclass_t ubser_devclass; static driver_t ubser_driver = { .name = "ubser", .methods = ubser_methods, .size = sizeof(struct ubser_softc), }; DRIVER_MODULE(ubser, uhub, ubser_driver, ubser_devclass, usbd_driver_load, 0); MODULE_DEPEND(ubser, usb, 1, 1, 1); MODULE_DEPEND(ubser, ucom, UCOM_MINVER, UCOM_PREFVER, UCOM_MAXVER); static int ubser_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); usb_interface_descriptor_t *id; usb_device_descriptor_t *dd; char buf[6]; usbd_status err; if (uaa->iface == NULL) return (UMATCH_NONE); dd = usbd_get_device_descriptor(uaa->device); if (dd == NULL) { return (UMATCH_NONE); } id = usbd_get_interface_descriptor(uaa->iface); if (id == NULL) { return (UMATCH_NONE); } err = usbreq_get_string_any(uaa->device, dd->iManufacturer, buf, sizeof(buf)); if (err != 0) return (UMATCH_NONE); /* check if this is a BWCT vendor specific ubser interface */ if ((strcmp(buf, "BWCT") == 0) && (id->bInterfaceClass == 0xff) && (id->bInterfaceSubClass == 0x00)) return (UMATCH_VENDOR_IFACESUBCLASS); return (UMATCH_NONE); } static int ubser_attach(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct ubser_softc *sc = device_get_softc(dev); usb_interface_descriptor_t *id; usb_device_request_t req; uint8_t n; int error; if (sc == NULL) { return ENOMEM; } usbd_set_desc(dev, uaa->device); snprintf(sc->sc_name, sizeof(sc->sc_name), "%s", device_get_nameunit(dev)); id = usbd_get_interface_descriptor(uaa->iface); sc->sc_iface_no = id->bInterfaceNumber; sc->sc_iface_index = uaa->iface_index; /* get number of serials */ req.bmRequestType = UT_READ_VENDOR_INTERFACE; req.bRequest = VENDOR_GET_NUMSER; USETW(req.wValue, 0); USETW(req.wIndex, sc->sc_iface_no); USETW(req.wLength, 1); error = usbd_do_request_flags(uaa->device, &req, &sc->sc_numser, 0, NULL, USBD_DEFAULT_TIMEOUT); if (error || (sc->sc_numser == 0)) { device_printf(dev, "failed to get number " "of serial ports: %s\n", usbd_errstr(error)); goto detach; } if (sc->sc_numser > UBSER_UNIT_MAX) sc->sc_numser = UBSER_UNIT_MAX; device_printf(dev, "found %i serials\n", sc->sc_numser); error = usbd_transfer_setup(uaa->device, sc->sc_iface_index, sc->sc_xfer, ubser_config, UBSER_TR_MAX, sc, &Giant); if (error) { goto detach; } sc->sc_tx_size = sc->sc_xfer[UBSER_TR_DT_WRITE]->length; if (sc->sc_tx_size == 0) { DPRINTF(sc, -1, "invalid tx_size!\n"); goto detach; } /* initialize port numbers */ for (n = 0; n < sc->sc_numser; n++) { sc->sc_ucom[n].sc_portno = n; } error = ucom_attach(sc->sc_ucom, sc->sc_numser, sc, &ubser_callback, &Giant); if (error) { goto detach; } mtx_lock(&Giant); sc->sc_flags |= (UBSER_FLAG_READ_STALL| UBSER_FLAG_WRITE_STALL); usbd_transfer_start(sc->sc_xfer[UBSER_TR_DT_READ]); mtx_unlock(&Giant); return 0; /* success */ detach: ubser_detach(dev); return ENXIO; /* failure */ } static int ubser_detach(device_t dev) { struct ubser_softc *sc = device_get_softc(dev); uint8_t n; DPRINTF(sc, 0, "\n"); ucom_detach(sc->sc_ucom, sc->sc_numser); /* need to stop all transfers atomically, * hence when clear stall completes, it * might start other transfers ! */ mtx_lock(&Giant); for (n = 0; n < UBSER_TR_MAX; n++) { if (sc->sc_xfer[n]) { usbd_transfer_stop(sc->sc_xfer[n]); } } mtx_unlock(&Giant); usbd_transfer_unsetup(sc->sc_xfer, UBSER_TR_MAX); return 0; } static int ubser_param(struct ucom_softc *ucom, struct termios *t) { struct ubser_softc *sc = ucom->sc_parent; DPRINTF(sc, 0, "\n"); /* * The firmware on our devices can only do 8n1@9600bps * without handshake. * We refuse to accept other configurations. */ /* enshure 9600bps */ switch (t->c_ospeed) { case 9600: break; default: return (EINVAL); } /* 2 stop bits not possible */ if (t->c_cflag & CSTOPB) return (EINVAL); /* XXX parity handling not possible with current firmware */ if (t->c_cflag & PARENB) return (EINVAL); /* we can only do 8 data bits */ switch (t->c_cflag & CSIZE) { case CS8: break; default: return (EINVAL); } /* we can't do any kind of hardware handshaking */ if ((t->c_cflag & (CRTS_IFLOW | CDTR_IFLOW |CDSR_OFLOW |CCAR_OFLOW)) != 0) return (EINVAL); /* * XXX xon/xoff not supported by the firmware! * This is handled within FreeBSD only and may overflow buffers * because of delayed reaction due to device buffering. */ return (0); } static __inline void ubser_inc_tx_unit(struct ubser_softc *sc) { sc->sc_curr_tx_unit ++; if (sc->sc_curr_tx_unit >= sc->sc_numser) { sc->sc_curr_tx_unit = 0; } return; } static void ubser_write_clear_stall_callback(struct usbd_xfer *xfer) { struct ubser_softc *sc = xfer->priv_sc; struct usbd_xfer *xfer_other = sc->sc_xfer[UBSER_TR_DT_WRITE]; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, xfer_other); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, xfer_other); sc->sc_flags &= ~UBSER_FLAG_WRITE_STALL; usbd_transfer_start(xfer_other); return; tr_error: sc->sc_flags &= ~UBSER_FLAG_WRITE_STALL; DPRINTF(sc, -1, "clear stall failed, error=%s\n", usbd_errstr(xfer->error)); return; } static void ubser_write_callback(struct usbd_xfer *xfer) { struct ubser_softc *sc = xfer->priv_sc; uint8_t *buf = xfer->buffer; uint8_t first_unit = sc->sc_curr_tx_unit; uint32_t actlen; USBD_CHECK_STATUS(xfer); tr_error: if (xfer->error != USBD_CANCELLED) { sc->sc_flags |= UBSER_FLAG_WRITE_STALL; usbd_transfer_start(sc->sc_xfer[UBSER_TR_CS_WRITE]); } return; tr_setup: tr_transferred: if (sc->sc_flags & UBSER_FLAG_WRITE_STALL) { usbd_transfer_start(sc->sc_xfer[UBSER_TR_CS_WRITE]); return; } do { if (ucom_get_data(sc->sc_ucom + sc->sc_curr_tx_unit, buf + 1, sc->sc_tx_size - 1, &actlen)) { buf[0] = sc->sc_curr_tx_unit; xfer->length = actlen + 1; usbd_start_hardware(xfer); ubser_inc_tx_unit(sc); /* round robin */ break; } ubser_inc_tx_unit(sc); } while (sc->sc_curr_tx_unit != first_unit); return; } static void ubser_read_clear_stall_callback(struct usbd_xfer *xfer) { struct ubser_softc *sc = xfer->priv_sc; struct usbd_xfer *xfer_other = sc->sc_xfer[UBSER_TR_DT_READ]; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, xfer_other); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, xfer_other); sc->sc_flags &= ~UBSER_FLAG_READ_STALL; usbd_transfer_start(xfer_other); return; tr_error: sc->sc_flags &= ~UBSER_FLAG_READ_STALL; DPRINTF(sc, -1, "clear stall failed, error=%s\n", usbd_errstr(xfer->error)); return; } static void ubser_read_callback(struct usbd_xfer *xfer) { struct ubser_softc *sc = xfer->priv_sc; uint8_t *buf = xfer->buffer; USBD_CHECK_STATUS(xfer); tr_error: if (xfer->error != USBD_CANCELLED) { sc->sc_flags |= UBSER_FLAG_READ_STALL; usbd_transfer_start(sc->sc_xfer[UBSER_TR_CS_READ]); } return; tr_transferred: if (xfer->actlen < 1) { DPRINTF(sc, 0, "invalid actlen=0!\n"); goto tr_setup; } if (buf[0] >= sc->sc_numser) { DPRINTF(sc, 0, "invalid serial number!\n"); goto tr_setup; } ucom_put_data(sc->sc_ucom + buf[0], buf + 1, xfer->actlen -1); tr_setup: if (sc->sc_flags & UBSER_FLAG_READ_STALL) { usbd_transfer_start(sc->sc_xfer[UBSER_TR_CS_READ]); } else { usbd_start_hardware(xfer); } return; } static void ubser_set_break(struct ucom_softc *ucom, u_int8_t onoff) { struct ubser_softc *sc = ucom->sc_parent; uint8_t x = ucom->sc_portno; if (onoff) { sc->sc_send_break[x/8] |= (1<<(x%8)); usbd_transfer_start(sc->sc_xfer[UBSER_TR_DT_BREAK]); } return; } static __inline void ubser_inc_break_unit(struct ubser_softc *sc) { sc->sc_curr_break_unit ++; if (sc->sc_curr_break_unit >= sc->sc_numser) { sc->sc_curr_break_unit = 0; } return; } static void ubser_send_break_callback(struct usbd_xfer *xfer) { struct ubser_softc *sc = xfer->priv_sc; usb_device_request_t *req = xfer->buffer; uint8_t first_unit = sc->sc_curr_break_unit; uint8_t x; USBD_CHECK_STATUS(xfer); tr_error: DPRINTF(sc, -1, "transfer error: %s\n", usbd_errstr(xfer->error)); tr_setup: tr_transferred: do { x = sc->sc_curr_break_unit; if (sc->sc_send_break[x/8] & (1<<(x%8))) { sc->sc_send_break[x/8] &= ~(1<<(x%8)); req->bmRequestType = UT_READ_VENDOR_INTERFACE; req->bRequest = VENDOR_SET_BREAK; USETW(req->wValue, x); USETW(req->wIndex, sc->sc_iface_no); USETW(req->wLength, 0); usbd_start_hardware(xfer); ubser_inc_break_unit(sc); /* round robin */ break; } ubser_inc_break_unit(sc); } while (sc->sc_curr_break_unit != first_unit); return; } static int ubser_open(struct ucom_softc *ucom) { /* fake status bits */ ucom->sc_mcr |= (SER_DTR | SER_RTS); ucom->sc_msr |= (SER_DCD); return 0; } static void ubser_start_write(struct ucom_softc *ucom) { struct ubser_softc *sc = ucom->sc_parent; usbd_transfer_start(sc->sc_xfer[UBSER_TR_DT_WRITE]); return; } pwcbsd/usb/ucom.c000644 000423 000000 00000046160 10551670754 014517 0ustar00luigiwheel000000 000000 /* $NetBSD: ucom.c,v 1.40 2001/11/13 06:24:54 lukem Exp $ */ /*- * Copyright (c) 2001-2003, 2005 Shunsuke Akiyama . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD: src/sys/dev/usb/ucom.c,v 1.60 2006/09/07 00:06:41 imp Exp $"); /*- * Copyright (c) 1998, 2000 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include __FBSDID("$FreeBSD: src/sys/dev/usb/ucom.c $"); #ifdef USB_DEBUG #define DPRINTF(n,fmt,...) \ do { if (ucom_debug > (n)) { \ printf("%s: " fmt, __FUNCTION__,## __VA_ARGS__); } } while (0) static int ucom_debug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, ucom, CTLFLAG_RW, 0, "USB ucom"); SYSCTL_INT(_hw_usb_ucom, OID_AUTO, debug, CTLFLAG_RW, &ucom_debug, 0, "ucom debug level"); #else #define DPRINTF(...) #endif static uint8_t ucom_units_alloc(uint32_t sub_units, uint32_t *p_root_unit); static void ucom_units_free(uint32_t root_unit, uint32_t sub_units); static int ucom_attach_sub(struct ucom_softc *sc); static void ucom_detach_sub(struct ucom_softc *sc); static void ucom_shutdown(struct ucom_softc *sc); static int ucom_open(struct tty *tp, struct cdev *dev); static void ucom_close(struct tty *tp); static int ucom_ioctl(struct tty *tp, u_long cmd, void *data, int flag, struct thread *td); static int ucom_modem(struct tty *tp, int sigon, int sigoff); static void ucom_break(struct tty *tp, int onoff); static void ucom_dtr(struct ucom_softc *sc, u_int8_t onoff); static void ucom_rts(struct ucom_softc *sc, u_int8_t onoff); static void ucom_notify(void *arg, int pending); static int ucom_param(struct tty *tp, struct termios *t); static void ucom_start_write(struct tty *tp); static void ucom_stop_write(struct tty *tp, int fflags); static moduledata_t ucom_mod = { "ucom", NULL, NULL }; DECLARE_MODULE(ucom, ucom_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); MODULE_DEPEND(ucom, usb, 1, 1, 1); MODULE_VERSION(ucom, UCOM_MODVER); #define UCOM_UNIT_MAX 0x1000 /* exclusive */ static uint8_t ucom_bitmap[(UCOM_UNIT_MAX+7)/8]; static uint8_t ucom_units_alloc(uint32_t sub_units, uint32_t *p_root_unit) { uint32_t n; uint32_t o; uint32_t x; uint32_t max = UCOM_UNIT_MAX - (UCOM_UNIT_MAX % sub_units); uint8_t error = 1; mtx_lock(&Giant); for (n = 0; n < max; n += sub_units) { /* check for free consecutive bits */ for (o = 0; o < sub_units; o++) { x = n + o; if (ucom_bitmap[x/8] & (1<< (x % 8))) { goto skip; } } /* allocate */ for (o = 0; o < sub_units; o++) { x = n + o; ucom_bitmap[x/8] |= (1<< (x % 8)); } *p_root_unit = n; error = 0; break; skip: ; } mtx_unlock(&Giant); return error; } static void ucom_units_free(uint32_t root_unit, uint32_t sub_units) { uint32_t x; mtx_lock(&Giant); while(sub_units--) { x = root_unit + sub_units; ucom_bitmap[x/8] &= ~(1<<(x%8)); } mtx_unlock(&Giant); return; } /* * "N" sub_units are setup at a time. All sub-units will * be given sequential unit numbers. The number of * sub-units can be used to differentiate among * different types of devices. * * The mutex pointed to by "p_mtx" is applied before all * callbacks are called back. Also "p_mtx" must be applied * before calling into the ucom-layer! Currently only Giant * is supported. */ int ucom_attach(struct ucom_softc *sc, uint32_t sub_units, void *parent, const struct ucom_callback *callback, struct mtx *p_mtx) { uint32_t n; uint32_t root_unit; int error = 0; if ((p_mtx != &Giant) || /* XXX TTY layer requires Giant */ (sc == NULL) || (sub_units == 0) || (callback == NULL)) { return EINVAL; } if (ucom_units_alloc(sub_units, &root_unit)) { return ENOMEM; } for (n = 0; n < sub_units; n++, sc++) { sc->sc_unit = root_unit + n; sc->sc_parent_mtx = p_mtx; sc->sc_parent = parent; sc->sc_callback = callback; mtx_lock(&Giant); /* XXX TTY layer */ error = ucom_attach_sub(sc); mtx_unlock(&Giant); /* XXX TTY layer */ if (error) { ucom_detach(sc - n, n); ucom_units_free(root_unit + n, sub_units - n); break; } sc->sc_flag |= UCOM_FLAG_ATTACHED; } return error; } /* NOTE: the following function will do nothing if * structure pointed to by "sc" is zero. */ void ucom_detach(struct ucom_softc *sc, uint32_t sub_units) { uint32_t n; for (n = 0; n < sub_units; n++, sc++) { if (sc->sc_flag & UCOM_FLAG_ATTACHED) { mtx_lock(&Giant); /* XXX TTY layer */ ucom_detach_sub(sc); mtx_unlock(&Giant); /* XXX TTY layer */ ucom_units_free(sc->sc_unit, 1); /* avoid duplicate detach: */ sc->sc_flag &= ~UCOM_FLAG_ATTACHED; } } return; } static int ucom_attach_sub(struct ucom_softc *sc) { struct tty *tp; int error = 0; tp = ttyalloc(); if (tp == NULL) { error = ENOMEM; goto done; } tp->t_sc = sc; tp->t_oproc = ucom_start_write; tp->t_param = ucom_param; tp->t_stop = ucom_stop_write; tp->t_break = ucom_break; tp->t_open = ucom_open; tp->t_close = ucom_close; tp->t_modem = ucom_modem; tp->t_ioctl = ucom_ioctl; DPRINTF(0, "tp = %p, unit = %d\n", tp, sc->sc_unit); #if !(defined(TS_CALLOUT) || (__FreeBSD_version >= 700022)) #define TS_CALLOUT NULL, sc->sc_unit, MINOR_CALLOUT /* compile fix for FreeBSD 6.x */ #endif error = ttycreate(tp, TS_CALLOUT, "U%d", sc->sc_unit); if (error) { ttyfree(tp); goto done; } sc->sc_tty = tp; TASK_INIT(&(sc->sc_task), 0, &ucom_notify, sc); DPRINTF(0, "ttycreate: ttyU%d\n", sc->sc_unit); done: return error; } static void ucom_detach_sub(struct ucom_softc *sc) { struct tty *tp = sc->sc_tty; DPRINTF(0, "sc = %p, tp = %p\n", sc, sc->sc_tty); sc->sc_flag |= UCOM_FLAG_GONE; sc->sc_flag &= ~(UCOM_FLAG_READ_ON| UCOM_FLAG_WRITE_ON); if (tp) { ttygone(tp); if (tp->t_state & TS_ISOPEN) { ucom_close(tp); } /* make sure that read and write * transfers are stopped */ if (sc->sc_callback->ucom_stop_read) { (sc->sc_callback->ucom_stop_read)(sc); } if (sc->sc_callback->ucom_stop_write) { (sc->sc_callback->ucom_stop_write)(sc); } taskqueue_drain(taskqueue_swi_giant, &(sc->sc_task)); ttyfree(tp); } return; } static void ucom_shutdown(struct ucom_softc *sc) { struct tty *tp = sc->sc_tty; mtx_assert(&Giant, MA_OWNED); DPRINTF(0, "\n"); /* * Hang up if necessary: */ if (tp->t_cflag & HUPCL) { ucom_modem(tp, 0, SER_DTR); } return; } static int ucom_open(struct tty *tp, struct cdev *dev) { struct ucom_softc *sc = tp->t_sc; int error; mtx_assert(&Giant, MA_OWNED); if (sc->sc_flag & UCOM_FLAG_GONE) { return ENXIO; } /* * wait a little for previous commands * to be flushed out: */ error = ttysleep(tp, tp, PCATCH | TTIPRI, "ucomsd", hz); if (error && (error != EWOULDBLOCK)) { return error; } DPRINTF(0, "tp = %p\n", tp); sc->sc_poll = 0; sc->sc_lsr = 0; sc->sc_msr = 0; sc->sc_mcr = 0; ucom_modem(tp, SER_DTR | SER_RTS, 0); if (sc->sc_callback->ucom_open) { error = (sc->sc_callback->ucom_open)(sc); if (error) { ucom_shutdown(sc); return error; } } sc->sc_flag |= UCOM_FLAG_READ_ON; if (sc->sc_callback->ucom_start_read) { (sc->sc_callback->ucom_start_read)(sc); } sc->sc_poll = 1; return 0; } static void ucom_close(struct tty *tp) { struct ucom_softc *sc = tp->t_sc; mtx_assert(&Giant, MA_OWNED); DPRINTF(0, "tp=%p\n", tp); tp->t_state &= ~TS_BUSY; ucom_shutdown(sc); sc->sc_flag &= ~(UCOM_FLAG_READ_ON| UCOM_FLAG_WRITE_ON); if (sc->sc_callback->ucom_stop_read) { (sc->sc_callback->ucom_stop_read)(sc); } if (sc->sc_callback->ucom_stop_write) { (sc->sc_callback->ucom_stop_write)(sc); } if (sc->sc_callback->ucom_close) { (sc->sc_callback->ucom_close)(sc); } return; } static int ucom_ioctl(struct tty *tp, u_long cmd, void *data, int flag, struct thread *td) { struct ucom_softc *sc = tp->t_sc; int error; mtx_assert(&Giant, MA_OWNED); if (sc->sc_flag & UCOM_FLAG_GONE) { return EIO; } DPRINTF(0, "cmd = 0x%08lx\n", cmd); error = ENOTTY; if (sc->sc_callback->ucom_ioctl) { error = (sc->sc_callback->ucom_ioctl)(sc, cmd, data, flag, td); } return error; } static int ucom_modem(struct tty *tp, int sigon, int sigoff) { struct ucom_softc *sc = tp->t_sc; u_int8_t onoff; mtx_assert(&Giant, MA_OWNED); if (sc->sc_flag & UCOM_FLAG_GONE) { return 0; } if ((sigon == 0) && (sigoff == 0)) { if (sc->sc_mcr & SER_DTR) { sigon |= SER_DTR; } if (sc->sc_mcr & SER_RTS) { sigon |= SER_RTS; } if (sc->sc_msr & SER_CTS) { sigon |= SER_CTS; } if (sc->sc_msr & SER_DCD) { sigon |= SER_DCD; } if (sc->sc_msr & SER_DSR) { sigon |= SER_DSR; } if (sc->sc_msr & SER_RI) { sigon |= SER_RI; } return sigon; } if (sigon & SER_DTR) { sc->sc_mcr |= SER_DTR; } if (sigoff & SER_DTR) { sc->sc_mcr &= ~SER_DTR; } if (sigon & SER_RTS) { sc->sc_mcr |= SER_RTS; } if (sigoff & SER_RTS) { sc->sc_mcr &= ~SER_RTS; } onoff = (sc->sc_mcr & SER_DTR) ? 1 : 0; ucom_dtr(sc, onoff); onoff = (sc->sc_mcr & SER_RTS) ? 1 : 0; ucom_rts(sc, onoff); return 0; } static void ucom_break(struct tty *tp, int onoff) { struct ucom_softc *sc = tp->t_sc; mtx_assert(&Giant, MA_OWNED); if (sc->sc_flag & UCOM_FLAG_GONE) { return; } DPRINTF(0, "onoff = %d\n", onoff); if (sc->sc_callback->ucom_set_break) { (sc->sc_callback->ucom_set_break)(sc, onoff); } return; } static void ucom_dtr(struct ucom_softc *sc, u_int8_t onoff) { DPRINTF(0, "onoff = %d\n", onoff); mtx_assert(&Giant, MA_OWNED); if (sc->sc_callback->ucom_set_dtr) { (sc->sc_callback->ucom_set_dtr)(sc, onoff); } return; } static void ucom_rts(struct ucom_softc *sc, u_int8_t onoff) { DPRINTF(0, "onoff = %d\n", onoff); mtx_assert(&Giant, MA_OWNED); if (sc->sc_callback->ucom_set_rts) { (sc->sc_callback->ucom_set_rts)(sc, onoff); } return; } void ucom_status_change(struct ucom_softc *sc) { u_int8_t old_msr; u_int8_t onoff; mtx_assert(&Giant, MA_OWNED); if (sc->sc_callback->ucom_get_status == NULL) { sc->sc_lsr = 0; sc->sc_msr = 0; return; } old_msr = sc->sc_msr; (sc->sc_callback->ucom_get_status)(sc, &(sc->sc_lsr), &(sc->sc_msr)); if ((sc->sc_msr ^ old_msr) & SER_DCD) { if (sc->sc_poll == 0) { return; } onoff = (sc->sc_msr & SER_DCD) ? 1 : 0; DPRINTF(0, "DCD changed to %d\n", onoff); sc->sc_last_status = onoff; /* deferred notifying to the TTY layer */ taskqueue_enqueue(taskqueue_swi_giant, &(sc->sc_task)); } return; } static void ucom_notify(void *arg, int pending) { struct ucom_softc *sc = arg; struct tty *tp = sc->sc_tty; ttyld_modem(tp, sc->sc_last_status); return; } static int ucom_param(struct tty *tp, struct termios *t) { struct ucom_softc *sc = tp->t_sc; int error; mtx_assert(&Giant, MA_OWNED); if (sc->sc_flag & UCOM_FLAG_GONE) { return EIO; } DPRINTF(0, "sc = %p\n", sc); /* Check requested parameters. */ if (t->c_ospeed < 0) { DPRINTF(0, "negative ospeed\n"); return EINVAL; } if (t->c_ispeed && (t->c_ispeed != t->c_ospeed)) { DPRINTF(0, "mismatch ispeed and ospeed\n"); return EINVAL; } /* * If there were no changes, don't do anything. This avoids dropping * input and improves performance when all we did was frob things like * VMIN and VTIME. */ if ((tp->t_ospeed == t->c_ospeed) && (tp->t_cflag == t->c_cflag)) { return 0; } /* And copy to tty. */ tp->t_ispeed = 0; tp->t_ospeed = t->c_ospeed; tp->t_cflag = t->c_cflag; if (sc->sc_callback->ucom_param == NULL) { return 0; } #if 0 sc->sc_flag &= ~UCOM_FLAG_READ_ON; if (sc->sc_callback->ucom_stop_read) { (sc->sc_callback->ucom_stop_read)(sc); } #endif error = (sc->sc_callback->ucom_param)(sc, t); if (error) { DPRINTF(0, "callback error = %d\n", error); return error; } ttsetwater(tp); if (t->c_cflag & CRTS_IFLOW) { sc->sc_flag |= UCOM_FLAG_RTS_IFLOW; } else if (sc->sc_flag & UCOM_FLAG_RTS_IFLOW) { sc->sc_flag &= ~UCOM_FLAG_RTS_IFLOW; ucom_modem(tp, SER_RTS, 0); } ttyldoptim(tp); #if 0 sc->sc_flag |= UCOM_FLAG_READ_ON; if (sc->sc_callback->ucom_start_read) { (sc->sc_callback->ucom_start_read)(sc); } #endif return 0; } static void ucom_start_write(struct tty *tp) { struct ucom_softc *sc = tp->t_sc; mtx_assert(&Giant, MA_OWNED); DPRINTF(0, "sc = %p\n", sc); if (sc->sc_flag & UCOM_FLAG_GONE) { return; } tp->t_state |= TS_BUSY; sc->sc_flag |= UCOM_FLAG_WRITE_ON; if (sc->sc_callback->ucom_start_write) { (sc->sc_callback->ucom_start_write)(sc); } return; } static void ucom_stop_write(struct tty *tp, int fflags) { struct ucom_softc *sc = tp->t_sc; mtx_assert(&Giant, MA_OWNED); if (sc->sc_flag & UCOM_FLAG_GONE) { return; } DPRINTF(0, "fflags=%d\n", fflags); if (fflags & FWRITE) { DPRINTF(0, "write\n"); if (tp->t_state & TS_BUSY) { /* XXX do what? */ if (!(tp->t_state & TS_TTSTOP)) { tp->t_state |= TS_FLUSH; } } } ucom_start_write(tp); DPRINTF(0, "done\n"); return; } /* * the following function returns * 1 if data is available, else 0 */ u_int8_t ucom_get_data(struct ucom_softc *sc, u_int8_t *buf, u_int32_t len, u_int32_t *actlen) { struct tty *tp = sc->sc_tty; u_int32_t cnt; mtx_assert(&Giant, MA_OWNED); actlen[0] = 0; if (!(sc->sc_flag & UCOM_FLAG_WRITE_ON)) { return 0; /* multiport device polling */ } if (tp->t_state & TS_TBLOCK) { if ((sc->sc_mcr & SER_RTS) && (sc->sc_flag & UCOM_FLAG_RTS_IFLOW)) { DPRINTF(0, "clear RTS\n"); ucom_modem(tp, 0, SER_RTS); } } else { if (!(sc->sc_mcr & SER_RTS) && (tp->t_rawq.c_cc <= tp->t_ilowat) && (sc->sc_flag & UCOM_FLAG_RTS_IFLOW)) { DPRINTF(0, "set RTS\n"); ucom_modem(tp, SER_RTS, 0); } } if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) { DPRINTF(0, "stopped\n"); goto done; } cnt = q_to_b(&(tp->t_outq), buf, len); if (cnt > len) { DPRINTF(0, "invalid length, %d bytes\n", cnt); goto done; } if (cnt == 0) { DPRINTF(0, "cnt == 0\n"); goto done; } actlen[0] = cnt; ttwwakeup(tp); return 1; done: tp->t_state &= ~(TS_BUSY | TS_FLUSH); ttwwakeup(tp); return 0; } void ucom_put_data(struct ucom_softc *sc, u_int8_t *ptr, u_int16_t len) { struct tty *tp = sc->sc_tty; u_int16_t lostcc; mtx_assert(&Giant, MA_OWNED); if (!(sc->sc_flag & UCOM_FLAG_READ_ON)) { return; /* multiport device polling */ } /* set a flag to prevent recursation */ if (len == 0) { goto done; } if (tp->t_state & TS_CAN_BYPASS_L_RINT) { if (((tp->t_rawq.c_cc + len) > tp->t_ihiwat) && ((sc->sc_flag & UCOM_FLAG_RTS_IFLOW) || (tp->t_iflag & IXOFF)) && (!(tp->t_state & TS_TBLOCK))) { ttyblock(tp); } lostcc = b_to_q(ptr, len, &(tp->t_rawq)); tp->t_rawcc += len; ttwakeup(tp); if ((tp->t_state & TS_TTSTOP) && ((tp->t_iflag & IXANY) || (tp->t_cc[VSTART] == tp->t_cc[VSTOP]))) { tp->t_state &= ~TS_TTSTOP; tp->t_lflag &= ~FLUSHO; ucom_start_write(tp); } if (lostcc > 0) { DPRINTF(0, "tp=%p, lost %d " "chars\n", tp, lostcc); } } else { /* pass characters to tty layer */ while (len) { DPRINTF(7, "char = 0x%02x\n", *ptr); if (ttyld_rint(tp, *ptr) == -1) { /* XXX what should we do? */ DPRINTF(0, "tp=%p, lost %d " "chars\n", tp, len); break; } len--; ptr++; } } done: if ((sc->sc_flag & UCOM_FLAG_RTS_IFLOW) && (!(sc->sc_mcr & SER_RTS)) && (!(tp->t_state & TS_TBLOCK))) { ucom_modem(tp, SER_RTS, 0); } return; } pwcbsd/usb/ucomvar.h000644 000423 000000 00000013425 10551670754 015233 0ustar00luigiwheel000000 000000 /* $NetBSD: ucomvar.h,v 1.9 2001/01/23 21:56:17 augustss Exp $ */ /* $FreeBSD: src/sys/dev/usb/ucomvar.h,v 1.8 2006/09/07 00:06:41 imp Exp $ */ /*- * Copyright (c) 2001-2002, Shunsuke Akiyama . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /*- * Copyright (c) 1999 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* Module interface related macros */ #define UCOM_MODVER 1 #define UCOM_MINVER 1 #define UCOM_PREFVER UCOM_MODVER #define UCOM_MAXVER 1 struct ucom_softc; struct thread; struct ucom_callback { void (*ucom_get_status)(struct ucom_softc *, uint8_t *, uint8_t *); void (*ucom_set_dtr)(struct ucom_softc *, uint8_t); void (*ucom_set_rts)(struct ucom_softc *, uint8_t); void (*ucom_set_break)(struct ucom_softc *, uint8_t); int (*ucom_param)(struct ucom_softc *, struct termios *); int (*ucom_ioctl)(struct ucom_softc *, uint32_t, caddr_t, int, struct thread *); int (*ucom_open)(struct ucom_softc *); void (*ucom_close)(struct ucom_softc *); void (*ucom_start_read)(struct ucom_softc *); void (*ucom_stop_read)(struct ucom_softc *); void (*ucom_start_write)(struct ucom_softc *); void (*ucom_stop_write)(struct ucom_softc *); }; /* Line status register */ #define ULSR_RCV_FIFO 0x80 #define ULSR_TSRE 0x40 /* Transmitter empty: byte sent */ #define ULSR_TXRDY 0x20 /* Transmitter buffer empty */ #define ULSR_BI 0x10 /* Break detected */ #define ULSR_FE 0x08 /* Framing error: bad stop bit */ #define ULSR_PE 0x04 /* Parity error */ #define ULSR_OE 0x02 /* Overrun, lost incoming byte */ #define ULSR_RXRDY 0x01 /* Byte ready in Receive Buffer */ #define ULSR_RCV_MASK 0x1f /* Mask for incoming data or error */ struct ucom_softc { struct task sc_task; const struct ucom_callback *sc_callback; struct tty *sc_tty; struct mtx *sc_parent_mtx; void *sc_parent; uint32_t sc_unit; uint16_t sc_portno; uint8_t sc_flag; #define UCOM_FLAG_RTS_IFLOW 0x01 /* use RTS input flow control */ #define UCOM_FLAG_GONE 0x02 /* the device is gone */ #define UCOM_FLAG_ATTACHED 0x04 /* set if attached */ #define UCOM_FLAG_READ_ON 0x08 /* set if read is enabled */ #define UCOM_FLAG_WRITE_ON 0x10 /* set if write is enabled */ uint8_t sc_lsr; uint8_t sc_msr; uint8_t sc_mcr; uint8_t sc_poll; uint8_t sc_last_status; }; int ucom_attach(struct ucom_softc *sc, uint32_t sub_units, void *parent, const struct ucom_callback *callback, struct mtx *p_mtx); void ucom_detach(struct ucom_softc *sc, uint32_t sub_units); void ucom_status_change(struct ucom_softc *); uint8_t ucom_get_data(struct ucom_softc *sc, uint8_t *buf, uint32_t len, uint32_t *actlen); void ucom_put_data(struct ucom_softc *sc, uint8_t *ptr, uint16_t len); pwcbsd/usb/ucycom.c000644 000423 000000 00000037077 10551670754 015062 0ustar00luigiwheel000000 000000 /*- * Copyright (c) 2004 Dag-Erling Coïdan Smørgrav * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD: src/sys/dev/usb/ucycom.c,v 1.4 2005/10/16 20:22:56 phk Exp $ */ /* * Device driver for Cypress CY7C637xx and CY7C640/1xx series USB to * RS232 bridges. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" #include __FBSDID("$FreeBSD: src/sys/dev/usb/ucycom.c,v 1.4 2005/10/16 20:22:56 phk Exp $"); #define UCYCOM_MAX_IOLEN (256 + 2) /* bytes */ #define UCYCOM_ENDPT_MAX 4 /* units */ #define UCYCOM_IFACE_INDEX 0 #define DPRINTF(...) struct ucycom_softc { struct ucom_softc sc_ucom; struct usbd_device * sc_udev; struct usbd_xfer * sc_xfer[UCYCOM_ENDPT_MAX]; device_t sc_dev; u_int32_t sc_model; #define MODEL_CY7C63743 0x63743 #define MODEL_CY7C64013 0x64013 u_int32_t sc_baud; u_int32_t sc_unit; u_int16_t sc_flen; /* feature report length */ u_int16_t sc_ilen; /* input report length */ u_int16_t sc_olen; /* output report length */ u_int8_t sc_fid; /* feature report id */ u_int8_t sc_iid; /* input report id */ u_int8_t sc_oid; /* output report id */ u_int8_t sc_cfg; #define UCYCOM_CFG_RESET 0x80 #define UCYCOM_CFG_PARODD 0x20 #define UCYCOM_CFG_PAREN 0x10 #define UCYCOM_CFG_STOPB 0x08 #define UCYCOM_CFG_DATAB 0x03 u_int8_t sc_ist; /* status flags from last input */ u_int8_t sc_flags; #define UCYCOM_FLAG_RELOAD_CONFIG 0x01 #define UCYCOM_FLAG_INTR_STALL 0x02 u_int8_t sc_name[16]; u_int8_t sc_iface_no; }; /* prototypes */ static device_probe_t ucycom_probe; static device_attach_t ucycom_attach; static device_detach_t ucycom_detach; static int ucycom_open(struct ucom_softc *ucom); static void ucycom_start_read(struct ucom_softc *ucom); static void ucycom_stop_read(struct ucom_softc *ucom); static void ucycom_start_write(struct ucom_softc *ucom); static void ucycom_stop_write(struct ucom_softc *ucom); static void ucycom_ctrl_write_callback(struct usbd_xfer *xfer); static void ucycom_config_callback(struct usbd_xfer *xfer); static int ucycom_param(struct ucom_softc *ucom, struct termios *t); static int ucycom_configure(struct ucycom_softc *sc, u_int32_t baud, u_int8_t cfg); static void ucycom_intr_read_clear_stall_callback(struct usbd_xfer *xfer); static void ucycom_intr_read_callback(struct usbd_xfer *xfer); static const struct usbd_config ucycom_config[UCYCOM_ENDPT_MAX] = { [0] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = (sizeof(usb_device_request_t) + UCYCOM_MAX_IOLEN), .flags = 0, .callback = &ucycom_ctrl_write_callback, .timeout = 1000, /* 1 second */ }, [1] = { .type = UE_INTERRUPT, .endpoint = -1, /* any */ .direction = UE_DIR_IN, .flags = USBD_SHORT_XFER_OK, .bufsize = UCYCOM_MAX_IOLEN, .callback = &ucycom_intr_read_callback, }, [2] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = (sizeof(usb_device_request_t) + UCYCOM_MAX_IOLEN), .flags = 0, .callback = &ucycom_config_callback, .timeout = 1000, /* 1 second */ }, [3] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .flags = USBD_USE_DMA, .callback = &ucycom_intr_read_clear_stall_callback, .timeout = 1000, /* 1 second */ }, }; static const struct ucom_callback ucycom_callback = { .ucom_param = &ucycom_param, .ucom_open = &ucycom_open, .ucom_start_read = &ucycom_start_read, .ucom_stop_read = &ucycom_stop_read, .ucom_start_write = &ucycom_start_write, .ucom_stop_write = &ucycom_stop_write, }; static device_method_t ucycom_methods[] = { DEVMETHOD(device_probe, ucycom_probe), DEVMETHOD(device_attach, ucycom_attach), DEVMETHOD(device_detach, ucycom_detach), { 0, 0 } }; static devclass_t ucycom_devclass; static driver_t ucycom_driver = { .name = "ucycom", .methods = ucycom_methods, .size = sizeof(struct ucycom_softc), }; DRIVER_MODULE(ucycom, uhub, ucycom_driver, ucycom_devclass, usbd_driver_load, 0); MODULE_VERSION(ucycom, 1); MODULE_DEPEND(ucycom, usb, 1, 1, 1); /* * Supported devices */ static struct ucycom_device { uint16_t vendor; uint16_t product; u_int32_t model; } ucycom_devices[] = { { USB_VENDOR_DELORME, USB_PRODUCT_DELORME_EARTHMATE, MODEL_CY7C64013 }, { 0, 0, 0 }, }; #define UCYCOM_DEFAULT_RATE 4800 #define UCYCOM_DEFAULT_CFG 0x03 /* N-8-1 */ static int ucycom_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct ucycom_device *ud; if (uaa->iface != NULL) { return UMATCH_NONE; } for (ud = ucycom_devices; ud->model != 0; ++ud) { if ((ud->vendor == uaa->vendor) && (ud->product == uaa->product)) { return UMATCH_VENDOR_PRODUCT; } } return UMATCH_NONE; } static int ucycom_attach(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct ucycom_softc *sc = device_get_softc(dev); struct ucycom_device *ud; struct usbd_interface *iface; void *urd_ptr = NULL; int32_t error; int32_t urd_len; if (sc == NULL) { return ENOMEM; } sc->sc_udev = uaa->device; sc->sc_dev = dev; sc->sc_unit = device_get_unit(dev); usbd_set_desc(dev, uaa->device); snprintf(sc->sc_name, sizeof(sc->sc_name), "%s", device_get_nameunit(dev)); DPRINTF(sc, 0, "\n"); /* get chip model */ for (ud = ucycom_devices; ud->model != 0; ++ud) { if ((ud->vendor == uaa->vendor) && (ud->product == uaa->product)) { sc->sc_model = ud->model; } } if (sc->sc_model == 0) { device_printf(dev, "unsupported device\n"); goto detach; } device_printf(dev, "Cypress CY7C%X USB to RS232 bridge\n", sc->sc_model); /* select configuration */ error = usbd_set_config_index(sc->sc_udev, 0, 1 /* verbose */); if (error) { device_printf(dev, "failed to select " "configuration: %s\n", usbd_errstr(error)); goto detach; } /* get report descriptor */ error = usbreq_read_report_desc(uaa->device, UCYCOM_IFACE_INDEX, &urd_ptr, &urd_len, M_USBDEV); if (error) { device_printf(dev, "failed to get report " "descriptor: %s\n", usbd_errstr(error)); goto detach; } /* get report sizes */ sc->sc_flen = hid_report_size(urd_ptr, urd_len, hid_feature, &sc->sc_fid); sc->sc_ilen = hid_report_size(urd_ptr, urd_len, hid_input, &sc->sc_iid); sc->sc_olen = hid_report_size(urd_ptr, urd_len, hid_output, &sc->sc_oid); if ((sc->sc_ilen > UCYCOM_MAX_IOLEN) || (sc->sc_ilen < 1) || (sc->sc_olen > UCYCOM_MAX_IOLEN) || (sc->sc_olen < 2) || (sc->sc_flen > UCYCOM_MAX_IOLEN) || (sc->sc_flen < 5)) { device_printf(dev, "invalid report size i=%d, o=%d, f=%d, max=%d\n", sc->sc_ilen, sc->sc_olen, sc->sc_flen, UCYCOM_MAX_IOLEN); goto detach; } iface = usbd_get_iface(uaa->device, UCYCOM_IFACE_INDEX); if (iface == NULL) { device_printf(dev, "no interface!\n"); goto detach; } if (iface->idesc == NULL) { device_printf(dev, "no interface descriptor!\n"); goto detach; } sc->sc_iface_no = iface->idesc->bInterfaceNumber; error = usbd_transfer_setup(uaa->device, UCYCOM_IFACE_INDEX, sc->sc_xfer, ucycom_config, UCYCOM_ENDPT_MAX, sc, &Giant); if (error) { device_printf(dev, "allocating USB " "transfers failed!\n"); goto detach; } error = ucom_attach(&(sc->sc_ucom), 1, sc, &ucycom_callback, &Giant); if (error) { goto detach; } if (urd_ptr) { free(urd_ptr, M_USBDEV); } return 0; /* success */ detach: if (urd_ptr) { free(urd_ptr, M_USBDEV); } ucycom_detach(dev); return ENXIO; } static int ucycom_detach(device_t dev) { struct ucycom_softc *sc = device_get_softc(dev); ucom_detach(&(sc->sc_ucom), 1); usbd_transfer_unsetup(sc->sc_xfer, UCYCOM_ENDPT_MAX); return 0; } static int ucycom_open(struct ucom_softc *ucom) { struct ucycom_softc *sc = ucom->sc_parent; /* set default configuration */ ucycom_configure(sc, UCYCOM_DEFAULT_RATE, UCYCOM_DEFAULT_CFG); sc->sc_flags |= UCYCOM_FLAG_INTR_STALL; return 0; } static void ucycom_start_read(struct ucom_softc *ucom) { struct ucycom_softc *sc = ucom->sc_parent; usbd_transfer_start(sc->sc_xfer[1]); return; } static void ucycom_stop_read(struct ucom_softc *ucom) { struct ucycom_softc *sc = ucom->sc_parent; usbd_transfer_stop(sc->sc_xfer[3]); usbd_transfer_stop(sc->sc_xfer[1]); return; } static void ucycom_start_write(struct ucom_softc *ucom) { struct ucycom_softc *sc = ucom->sc_parent; usbd_transfer_start(sc->sc_xfer[0]); return; } static void ucycom_stop_write(struct ucom_softc *ucom) { struct ucycom_softc *sc = ucom->sc_parent; usbd_transfer_stop(sc->sc_xfer[0]); return; } static void ucycom_ctrl_write_callback(struct usbd_xfer *xfer) { struct ucycom_softc *sc = xfer->priv_sc; usb_device_request_t *req = xfer->buffer; u_int8_t offset; u_int32_t actlen; USBD_CHECK_STATUS(xfer); tr_error: if (xfer->error == USBD_CANCELLED) { return; } DPRINTF(sc, 0, "error=%s\n", usbd_errstr(xfer->error)); tr_transferred: tr_setup: switch (sc->sc_model) { case MODEL_CY7C63743: offset = 1; break; case MODEL_CY7C64013: offset = 2; break; default: offset = 0; break; } if(ucom_get_data(&(sc->sc_ucom), req->bData + offset, sc->sc_olen - offset, &actlen)) { req->bmRequestType = UT_WRITE_CLASS_INTERFACE; req->bRequest = UR_SET_REPORT; USETW2(req->wValue, UHID_OUTPUT_REPORT, sc->sc_oid); USETW(req->wIndex, sc->sc_iface_no); USETW(req->wLength, sc->sc_olen); switch (sc->sc_model) { case MODEL_CY7C63743: req->bData[0] = actlen; break; case MODEL_CY7C64013: req->bData[0] = 0; req->bData[1] = actlen; break; default: panic("invalid model number!\n"); break; } xfer->length = (sc->sc_olen + sizeof(*req)); usbd_start_hardware(xfer); } return; } static void ucycom_config_callback(struct usbd_xfer *xfer) { struct ucycom_softc *sc = xfer->priv_sc; usb_device_request_t *req = xfer->buffer; USBD_CHECK_STATUS(xfer); tr_error: if (xfer->error == USBD_CANCELLED) { return; } DPRINTF(sc, 0, "error=%s\n", usbd_errstr(xfer->error)); tr_transferred: tr_setup: if (sc->sc_flags & UCYCOM_FLAG_RELOAD_CONFIG) { sc->sc_flags &= ~UCYCOM_FLAG_RELOAD_CONFIG; req->bmRequestType = UT_WRITE_CLASS_INTERFACE; req->bRequest = UR_SET_REPORT; USETW2(req->wValue, UHID_FEATURE_REPORT, sc->sc_fid); USETW(req->wIndex, sc->sc_iface_no); USETW(req->wLength, sc->sc_flen); req->bData[0] = (sc->sc_baud & 0xff); req->bData[1] = (sc->sc_baud >> 8) & 0xff; req->bData[2] = (sc->sc_baud >> 16) & 0xff; req->bData[3] = (sc->sc_baud >> 24) & 0xff; req->bData[4] = sc->sc_cfg; xfer->length = (sc->sc_flen + sizeof(*req)); usbd_start_hardware(xfer); } return; } static int ucycom_param(struct ucom_softc *ucom, struct termios *t) { struct ucycom_softc *sc = ucom->sc_parent; u_int32_t baud; u_int8_t cfg; int error; DPRINTF(sc, 0, "\n"); if (t->c_ispeed != t->c_ospeed) { return (EINVAL); } baud = t->c_ispeed; if (t->c_cflag & CIGNORE) { cfg = sc->sc_cfg; } else { cfg = 0; switch (t->c_cflag & CSIZE) { case CS8: ++cfg; case CS7: ++cfg; case CS6: ++cfg; case CS5: break; default: return (EINVAL); } if (t->c_cflag & CSTOPB) cfg |= UCYCOM_CFG_STOPB; if (t->c_cflag & PARENB) cfg |= UCYCOM_CFG_PAREN; if (t->c_cflag & PARODD) cfg |= UCYCOM_CFG_PARODD; } error = ucycom_configure(sc, baud, cfg); return error; } static int ucycom_configure(struct ucycom_softc *sc, u_int32_t baud, u_int8_t cfg) { switch (baud) { case 600: case 1200: case 2400: case 4800: case 9600: case 19200: case 38400: case 57600: #if 0 /* * Stock chips only support standard baud rates in the 600 - 57600 * range, but higher rates can be achieved using custom firmware. */ case 115200: case 153600: case 192000: #endif break; default: return EINVAL; } sc->sc_flags |= UCYCOM_FLAG_RELOAD_CONFIG; sc->sc_baud = baud; sc->sc_cfg = cfg; usbd_transfer_start(sc->sc_xfer[2]); return 0; } static void ucycom_intr_read_clear_stall_callback(struct usbd_xfer *xfer) { struct ucycom_softc *sc = xfer->priv_sc; struct usbd_xfer *xfer_other = sc->sc_xfer[1]; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, xfer_other); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, xfer_other); sc->sc_flags &= ~UCYCOM_FLAG_INTR_STALL; usbd_transfer_start(xfer_other); return; tr_error: sc->sc_flags &= ~UCYCOM_FLAG_INTR_STALL; DPRINTF(sc, 0, "clear stall failed, error=%s\n", usbd_errstr(xfer->error)); return; } static void ucycom_intr_read_callback(struct usbd_xfer *xfer) { struct ucycom_softc *sc = xfer->priv_sc; u_int8_t *ptr = xfer->buffer; u_int32_t len; USBD_CHECK_STATUS(xfer); tr_error: if (xfer->error != USBD_CANCELLED) { sc->sc_flags |= UCYCOM_FLAG_INTR_STALL; usbd_transfer_start(sc->sc_xfer[3]); } return; tr_transferred: switch (sc->sc_model) { case MODEL_CY7C63743: if (xfer->actlen < 1) { goto tr_setup; } sc->sc_ist = ptr[0] & ~0x07; len = ptr[0] & 0x07; xfer->actlen --; ptr ++; xfer->actlen = min(xfer->actlen, len); break; case MODEL_CY7C64013: if (xfer->actlen < 2) { goto tr_setup; } sc->sc_ist = ptr[0] & ~0x07; len = ptr[1]; xfer->actlen -= 2; ptr += 2; xfer->actlen = min(xfer->actlen, len); break; default: panic("unsupported model number!"); break; } if (xfer->actlen) { ucom_put_data(&(sc->sc_ucom), ptr, xfer->actlen); } tr_setup: if (sc->sc_flags & UCYCOM_FLAG_INTR_STALL) { usbd_transfer_start(sc->sc_xfer[3]); } else { xfer->length = sc->sc_ilen; usbd_start_hardware(xfer); } return; } pwcbsd/usb/udbp.c000644 000423 000000 00000053522 10551670754 014506 0ustar00luigiwheel000000 000000 /*- * Copyright (c) 1996-2000 Whistle Communications, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of author nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY NICK HIBMA AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD: src/sys/dev/usb/udbp.c,v 1.31 2006/09/07 00:06:41 imp Exp $"); /* Driver for arbitrary double bulk pipe devices. * The driver assumes that there will be the same driver on the other side. * * XXX Some more information on what the framing of the IP packets looks like. * * To take full advantage of bulk transmission, packets should be chosen * between 1k and 5k in size (1k to make sure the sending side starts * streaming, and <5k to avoid overflowing the system with small TDs). */ /* probe/attach/detach: * Connect the driver to the hardware and netgraph * * The reason we submit a bulk in transfer is that USB does not know about * interrupts. The bulk transfer continuously polls the device for data. * While the device has no data available, the device NAKs the TDs. As soon * as there is data, the transfer happens and the data comes flowing in. * * In case you were wondering, interrupt transfers happen exactly that way. * It therefore doesn't make sense to use the interrupt pipe to signal * 'data ready' and then schedule a bulk transfer to fetch it. That would * incur a 2ms delay at least, without reducing bandwidth requirements. * */ #include #include #include #include #include #include #include #include "usbdevs.h" #include #include #include #include #include #ifdef USB_DEBUG #define DPRINTF(sc,n,fmt,...) do { \ if (udbp_debug > (n)) \ printf("%s:%s: " fmt, (sc)->sc_name, \ __FUNCTION__,## __VA_ARGS__); \ } while(0) static int udbp_debug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, udbp, CTLFLAG_RW, 0, "USB udbp"); SYSCTL_INT(_hw_usb_udbp, OID_AUTO, debug, CTLFLAG_RW, &udbp_debug, 0, "udbp debug level"); #else #define DPRINTF(...) /* nop */ #endif #define UDBP_TIMEOUT 2000 /* timeout on outbound transfers, in msecs */ #define UDBP_BUFFERSIZE MCLBYTES /* maximum number of bytes in one transfer */ #define UDBP_T_WR 0 #define UDBP_T_RD 1 #define UDBP_T_WR_CS 2 #define UDBP_T_RD_CS 3 #define UDBP_T_MAX 4 #define UDBP_Q_MAXLEN 50 struct udbp_softc { struct mtx sc_mtx; struct ng_bt_mbufq sc_xmitq_hipri; /* hi-priority transmit queue */ struct ng_bt_mbufq sc_xmitq; /* low-priority transmit queue */ struct usbd_xfer * sc_xfer[UDBP_T_MAX]; node_p sc_node; /* back pointer to node */ hook_p sc_hook; /* pointer to the hook */ struct mbuf * sc_bulk_in_buffer; u_int32_t sc_packets_in; /* packets in from downstream */ u_int32_t sc_packets_out; /* packets out towards downstream */ u_int8_t sc_flags; #define UDBP_FLAG_READ_STALL 0x01 /* read transfer stalled */ #define UDBP_FLAG_WRITE_STALL 0x02 /* write transfer stalled */ u_int8_t sc_name[16]; }; static int udbp_modload(module_t mod, int event, void *data); static device_probe_t udbp_probe; static device_attach_t udbp_attach; static device_detach_t udbp_detach; static void udbp_bulk_read_callback(struct usbd_xfer *xfer); static void udbp_bulk_read_clear_stall_callback(struct usbd_xfer *xfer); static void udbp_bulk_read_complete(node_p node, hook_p hook, void *arg1, int arg2); static void udbp_bulk_write_callback(struct usbd_xfer *xfer); static void udbp_bulk_write_clear_stall_callback(struct usbd_xfer *xfer); static ng_constructor_t ng_udbp_constructor; static ng_rcvmsg_t ng_udbp_rcvmsg; static ng_shutdown_t ng_udbp_rmnode; static ng_newhook_t ng_udbp_newhook; static ng_connect_t ng_udbp_connect; static ng_rcvdata_t ng_udbp_rcvdata; static ng_disconnect_t ng_udbp_disconnect; /* Parse type for struct ngudbpstat */ static const struct ng_parse_struct_field ng_udbp_stat_type_fields[] = NG_UDBP_STATS_TYPE_INFO; static const struct ng_parse_type ng_udbp_stat_type = { &ng_parse_struct_type, &ng_udbp_stat_type_fields }; /* List of commands and how to convert arguments to/from ASCII */ static const struct ng_cmdlist ng_udbp_cmdlist[] = { { NGM_UDBP_COOKIE, NGM_UDBP_GET_STATUS, "getstatus", NULL, &ng_udbp_stat_type, }, { NGM_UDBP_COOKIE, NGM_UDBP_SET_FLAG, "setflag", &ng_parse_int32_type, NULL }, { 0 } }; /* Netgraph node type descriptor */ static struct ng_type ng_udbp_typestruct = { .version = NG_ABI_VERSION, .name = NG_UDBP_NODE_TYPE, .constructor = ng_udbp_constructor, .rcvmsg = ng_udbp_rcvmsg, .shutdown = ng_udbp_rmnode, .newhook = ng_udbp_newhook, .connect = ng_udbp_connect, .rcvdata = ng_udbp_rcvdata, .disconnect = ng_udbp_disconnect, .cmdlist = ng_udbp_cmdlist, }; /* USB config */ static const struct usbd_config udbp_config[UDBP_T_MAX] = { [UDBP_T_WR] = { .type = UE_BULK, .endpoint = -1, /* any */ .direction = UE_DIR_OUT, .bufsize = UDBP_BUFFERSIZE, .flags = USBD_USE_DMA|USBD_FORCE_SHORT_XFER, .callback = &udbp_bulk_write_callback, .timeout = UDBP_TIMEOUT, }, [UDBP_T_RD] = { .type = UE_BULK, .endpoint = -1, /* any */ .direction = UE_DIR_IN, .bufsize = UDBP_BUFFERSIZE, .flags = USBD_USE_DMA|USBD_SHORT_XFER_OK, .callback = &udbp_bulk_read_callback, }, [UDBP_T_WR_CS] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .flags = USBD_USE_DMA, .callback = &udbp_bulk_write_clear_stall_callback, .timeout = 1000, /* 1 second */ }, [UDBP_T_RD_CS] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .flags = USBD_USE_DMA, .callback = &udbp_bulk_read_clear_stall_callback, .timeout = 1000, /* 1 second */ }, }; static devclass_t udbp_devclass; static device_method_t udbp_methods[] = { /* Device interface */ DEVMETHOD(device_probe, udbp_probe), DEVMETHOD(device_attach, udbp_attach), DEVMETHOD(device_detach, udbp_detach), { 0, 0 } }; static driver_t udbp_driver = { .name = "udbp", .methods = udbp_methods, .size = sizeof(struct udbp_softc), }; DRIVER_MODULE(udbp, uhub, udbp_driver, udbp_devclass, udbp_modload, 0); MODULE_DEPEND(udbp, netgraph, NG_ABI_VERSION, NG_ABI_VERSION, NG_ABI_VERSION); MODULE_DEPEND(udbp, usb, 1,1,1); static int udbp_modload(module_t mod, int event, void *data) { int error; switch (event) { case MOD_LOAD: error = ng_newtype(&ng_udbp_typestruct); if (error != 0) { printf("%s: Could not register " "Netgraph node type, error=%d\n", NG_UDBP_NODE_TYPE, error); } else error = usbd_driver_load(mod, event, data); break; case MOD_UNLOAD: error = ng_rmtype(&ng_udbp_typestruct); if (error == 0) error = usbd_driver_load(mod, event, data); break; default: error = EOPNOTSUPP; break; } return (error); } static int udbp_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); if (!uaa->iface) return (UMATCH_NONE); /* XXX Julian, add the id of the device if you have one to test * things with. run 'usbdevs -v' and note the 3 ID's that appear. * The Vendor Id and Product Id are in hex and the Revision Id is in * bcd. But as usual if the revision is 0x101 then you should compare * the revision id in the device descriptor with 0x101 * Or go search the file usbdevs.h. Maybe the device is already in * there. */ if (((uaa->vendor == USB_VENDOR_NETCHIP) && (uaa->product == USB_PRODUCT_NETCHIP_TURBOCONNECT))) return(UMATCH_VENDOR_PRODUCT); if (((uaa->vendor == USB_VENDOR_PROLIFIC) && ((uaa->product == USB_PRODUCT_PROLIFIC_PL2301) || (uaa->product == USB_PRODUCT_PROLIFIC_PL2302)))) return(UMATCH_VENDOR_PRODUCT); if (((uaa->vendor == USB_VENDOR_ANCHOR) && (uaa->product == USB_PRODUCT_ANCHOR_EZLINK))) return(UMATCH_VENDOR_PRODUCT); return (UMATCH_NONE); } static int udbp_attach(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct udbp_softc *sc = device_get_softc(dev); int32_t error; if (sc == NULL) { return ENOMEM; } usbd_set_desc(dev, uaa->device); snprintf(sc->sc_name, sizeof(sc->sc_name), "%s", device_get_nameunit(dev)); mtx_init(&(sc->sc_mtx), "udbp lock", NULL, MTX_DEF|MTX_RECURSE); error = usbd_transfer_setup(uaa->device, uaa->iface_index, sc->sc_xfer, udbp_config, UDBP_T_MAX, sc, &(sc->sc_mtx)); if (error) { DPRINTF(sc, 0, "error=%s\n", usbd_errstr(error)) ; goto detach; } NG_BT_MBUFQ_INIT(&(sc->sc_xmitq), UDBP_Q_MAXLEN); NG_BT_MBUFQ_INIT(&(sc->sc_xmitq_hipri), UDBP_Q_MAXLEN); /* create Netgraph node */ if (ng_make_node_common(&ng_udbp_typestruct, &sc->sc_node) != 0) { printf("%s: Could not create Netgraph node\n", sc->sc_name); sc->sc_node = NULL; goto detach; } /* name node */ if (ng_name_node(sc->sc_node, sc->sc_name) != 0) { printf("%s: Could not name node\n", sc->sc_name); NG_NODE_UNREF(sc->sc_node); sc->sc_node = NULL; goto detach; } NG_NODE_SET_PRIVATE(sc->sc_node, sc); /* the device is now operational */ return 0; /* success */ detach: udbp_detach(dev); return ENOMEM; /* failure */ } static int udbp_detach(device_t dev) { struct udbp_softc *sc = device_get_softc(dev); /* destroy Netgraph node */ if (sc->sc_node != NULL) { NG_NODE_SET_PRIVATE(sc->sc_node, NULL); ng_rmnode_self(sc->sc_node); sc->sc_node = NULL; } /* free USB transfers, if any */ usbd_transfer_unsetup(sc->sc_xfer, UDBP_T_MAX); mtx_destroy(&(sc->sc_mtx)); /* destroy queues */ NG_BT_MBUFQ_DESTROY(&(sc->sc_xmitq)); NG_BT_MBUFQ_DESTROY(&(sc->sc_xmitq_hipri)); /* extra check */ if (sc->sc_bulk_in_buffer) { m_freem(sc->sc_bulk_in_buffer); sc->sc_bulk_in_buffer = NULL; } return 0; /* success */ } static void udbp_bulk_read_callback(struct usbd_xfer *xfer) { struct udbp_softc *sc = xfer->priv_sc; struct mbuf *m; USBD_CHECK_STATUS(xfer); tr_error: if (xfer->error != USBD_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= UDBP_FLAG_READ_STALL; usbd_transfer_start(sc->sc_xfer[UDBP_T_RD_CS]); } return; tr_transferred: /* allocate new mbuf */ MGETHDR(m, M_DONTWAIT, MT_DATA); if (m == NULL) { goto tr_setup; } MCLGET(m, M_DONTWAIT); if (!(m->m_flags & M_EXT)) { m_freem(m); goto tr_setup; } m->m_pkthdr.len = m->m_len = xfer->actlen; usbd_copy_out(&(xfer->buf_data), 0, m->m_data, xfer->actlen); sc->sc_bulk_in_buffer = m; DPRINTF(sc, 0, "received package %d " "bytes\n", xfer->actlen); tr_setup: if (sc->sc_bulk_in_buffer) { ng_send_fn(sc->sc_node, NULL, &udbp_bulk_read_complete, NULL, 0); return; } if (sc->sc_flags & UDBP_FLAG_READ_STALL) { usbd_transfer_start(sc->sc_xfer[UDBP_T_RD_CS]); return; } usbd_start_hardware(xfer); return; } static void udbp_bulk_read_clear_stall_callback(struct usbd_xfer *xfer) { struct udbp_softc *sc = xfer->priv_sc; struct usbd_xfer *other_xfer = sc->sc_xfer[UDBP_T_RD]; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, other_xfer); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, other_xfer); sc->sc_flags &= ~UDBP_FLAG_READ_STALL; usbd_transfer_start(other_xfer); return; tr_error: /* bomb out */ sc->sc_flags &= ~UDBP_FLAG_READ_STALL; DPRINTF(sc, 0, "clear stall failed!\n"); return; } static void udbp_bulk_read_complete(node_p node, hook_p hook, void *arg1, int arg2) { struct udbp_softc * sc = NG_NODE_PRIVATE(node); struct mbuf * m; int error; if (sc == NULL) { return; } mtx_lock(&(sc->sc_mtx)); m = sc->sc_bulk_in_buffer; if (m) { sc->sc_bulk_in_buffer = NULL; if ((sc->sc_hook == NULL) || NG_HOOK_NOT_VALID(sc->sc_hook)) { DPRINTF(sc, 0, "No upstream hook\n"); goto done; } sc->sc_packets_in ++; NG_SEND_DATA_ONLY(error, sc->sc_hook, m); m = NULL; } done: if (m) { m_freem(m); } /* start USB bulk-in transfer, if not already started */ usbd_transfer_start(sc->sc_xfer[UDBP_T_RD]); mtx_unlock(&(sc->sc_mtx)); return; } static void udbp_bulk_write_callback(struct usbd_xfer *xfer) { struct udbp_softc *sc = xfer->priv_sc; struct mbuf *m; USBD_CHECK_STATUS(xfer); tr_error: if (xfer->error != USBD_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= UDBP_FLAG_WRITE_STALL; usbd_transfer_start(sc->sc_xfer[UDBP_T_WR_CS]); } return; tr_transferred: sc->sc_packets_out ++; tr_setup: if (sc->sc_flags & UDBP_FLAG_WRITE_STALL) { usbd_transfer_start(sc->sc_xfer[UDBP_T_WR_CS]); return; } /* get next mbuf, if any */ NG_BT_MBUFQ_DEQUEUE(&(sc->sc_xmitq_hipri), m); if (m == NULL) { NG_BT_MBUFQ_DEQUEUE(&(sc->sc_xmitq), m); if (m == NULL) { DPRINTF(sc, 0, "Data queue is empty\n"); return; } } if (m->m_pkthdr.len > MCLBYTES) { DPRINTF(sc, 0, "truncating large packet " "from %d to %d bytes\n", m->m_pkthdr.len, MCLBYTES); m->m_pkthdr.len = MCLBYTES; } usbd_m_copy_in(&(xfer->buf_data), 0, m, 0, m->m_pkthdr.len); xfer->length = m->m_pkthdr.len; m_freem(m); DPRINTF(sc, 0, "packet out: %d bytes\n", xfer->length); usbd_start_hardware(xfer); return; } static void udbp_bulk_write_clear_stall_callback(struct usbd_xfer *xfer) { struct udbp_softc *sc = xfer->priv_sc; struct usbd_xfer *other_xfer = sc->sc_xfer[UDBP_T_WR]; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, other_xfer); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, other_xfer); sc->sc_flags &= ~UDBP_FLAG_WRITE_STALL; usbd_transfer_start(other_xfer); return; tr_error: /* bomb out */ sc->sc_flags &= ~UDBP_FLAG_WRITE_STALL; DPRINTF(sc, 0, "clear stall failed\n"); return; } /*********************************************************************** * Start of Netgraph methods **********************************************************************/ /* * If this is a device node so this work is done in the attach() * routine and the constructor will return EINVAL as you should not be able * to create nodes that depend on hardware (unless you can add the hardware :) */ static int ng_udbp_constructor(node_p node) { return (EINVAL); } /* * Give our ok for a hook to be added... * If we are not running this might kick a device into life. * Possibly decode information out of the hook name. * Add the hook's private info to the hook structure. * (if we had some). In this example, we assume that there is a * an array of structs, called 'channel' in the private info, * one for each active channel. The private * pointer of each hook points to the appropriate UDBP_hookinfo struct * so that the source of an input packet is easily identified. */ static int ng_udbp_newhook(node_p node, hook_p hook, const char *name) { struct udbp_softc *sc = NG_NODE_PRIVATE(node); int32_t error = 0; if (strcmp(name, NG_UDBP_HOOK_NAME)) { return EINVAL; } mtx_lock(&(sc->sc_mtx)); if (sc->sc_hook != NULL) { error = EISCONN; } else { sc->sc_hook = hook; NG_HOOK_SET_PRIVATE(hook, NULL); } mtx_unlock(&(sc->sc_mtx)); return error; } /* * Get a netgraph control message. * Check it is one we understand. If needed, send a response. * We could save the address for an async action later, but don't here. * Always free the message. * The response should be in a malloc'd region that the caller can 'free'. * A response is not required. * Theoretically you could respond defferently to old message types if * the cookie in the header didn't match what we consider to be current * (so that old userland programs could continue to work). */ static int ng_udbp_rcvmsg(node_p node, item_p item, hook_p lasthook) { struct udbp_softc *sc = NG_NODE_PRIVATE(node); struct ng_mesg *resp = NULL; int error = 0; struct ng_mesg *msg; NGI_GET_MSG(item, msg); /* Deal with message according to cookie and command */ switch (msg->header.typecookie) { case NGM_UDBP_COOKIE: switch (msg->header.cmd) { case NGM_UDBP_GET_STATUS: { struct ngudbpstat *stats; NG_MKRESPONSE(resp, msg, sizeof(*stats), M_NOWAIT); if (!resp) { error = ENOMEM; break; } stats = (struct ngudbpstat *) resp->data; mtx_lock(&(sc->sc_mtx)); stats->packets_in = sc->sc_packets_in; stats->packets_out = sc->sc_packets_out; mtx_unlock(&(sc->sc_mtx)); break; } case NGM_UDBP_SET_FLAG: if (msg->header.arglen != sizeof(u_int32_t)) { error = EINVAL; break; } DPRINTF(sc, 0, "flags = 0x%08x\n", *((u_int32_t *) msg->data)); break; default: error = EINVAL; /* unknown command */ break; } break; default: error = EINVAL; /* unknown cookie type */ break; } /* Take care of synchronous response, if any */ NG_RESPOND_MSG(error, node, item, resp); NG_FREE_MSG(msg); return(error); } /* * Accept data from the hook and queue it for output. */ static int ng_udbp_rcvdata(hook_p hook, item_p item) { struct udbp_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); struct ng_bt_mbufq *queue_ptr; struct mbuf *m; struct ng_tag_prio *ptag; int error; if (sc == NULL) { NG_FREE_ITEM(item); return EHOSTDOWN; } NGI_GET_M(item, m); NG_FREE_ITEM(item); /* * Now queue the data for when it can be sent */ ptag = (void *)m_tag_locate(m, NGM_GENERIC_COOKIE, NG_TAG_PRIO, NULL); if (ptag && (ptag->priority > NG_PRIO_CUTOFF)) queue_ptr = &(sc->sc_xmitq_hipri); else queue_ptr = &(sc->sc_xmitq); mtx_lock(&(sc->sc_mtx)); if (NG_BT_MBUFQ_FULL(queue_ptr)) { NG_BT_MBUFQ_DROP(queue_ptr); NG_FREE_M(m); error = ENOBUFS; } else { NG_BT_MBUFQ_ENQUEUE(queue_ptr, m); /* start bulk-out transfer, if not * already started: */ usbd_transfer_start(sc->sc_xfer[UDBP_T_WR]); error = 0; } mtx_unlock(&(sc->sc_mtx)); return error; } /* * Do local shutdown processing.. * We are a persistant device, we refuse to go away, and * only remove our links and reset ourself. */ static int ng_udbp_rmnode(node_p node) { struct udbp_softc *sc = NG_NODE_PRIVATE(node); /* Let old node go */ NG_NODE_SET_PRIVATE(node, NULL); NG_NODE_UNREF(node); /* forget it ever existed */ if (sc == NULL) { goto done; } /* Create Netgraph node */ if (ng_make_node_common(&ng_udbp_typestruct, &sc->sc_node) != 0) { printf("%s: Could not create Netgraph node\n", sc->sc_name); sc->sc_node = NULL; goto done; } /* Name node */ if (ng_name_node(sc->sc_node, sc->sc_name) != 0) { printf("%s: Could not name Netgraph node\n", sc->sc_name); NG_NODE_UNREF(sc->sc_node); sc->sc_node = NULL; goto done; } NG_NODE_SET_PRIVATE(sc->sc_node, sc); done: if (sc) { mtx_unlock(&(sc->sc_mtx)); } return 0; } /* * This is called once we've already connected a new hook to the other node. * It gives us a chance to balk at the last minute. */ static int ng_udbp_connect(hook_p hook) { struct udbp_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); /* probably not at splnet, force outward queueing */ NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook)); mtx_lock(&(sc->sc_mtx)); sc->sc_flags |= (UDBP_FLAG_READ_STALL| UDBP_FLAG_WRITE_STALL); /* start bulk-in transfer */ usbd_transfer_start(sc->sc_xfer[UDBP_T_RD]); /* start bulk-out transfer */ usbd_transfer_start(sc->sc_xfer[UDBP_T_WR]); mtx_unlock(&(sc->sc_mtx)); return (0); } /* * Dook disconnection * * For this type, removal of the last link destroys the node */ static int ng_udbp_disconnect(hook_p hook) { struct udbp_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); int error = 0; if (sc != NULL) { mtx_lock(&(sc->sc_mtx)); if (hook != sc->sc_hook) { error = EINVAL; } else { /* stop bulk-in transfer */ usbd_transfer_stop(sc->sc_xfer[UDBP_T_RD_CS]); usbd_transfer_stop(sc->sc_xfer[UDBP_T_RD]); /* stop bulk-out transfer */ usbd_transfer_stop(sc->sc_xfer[UDBP_T_WR_CS]); usbd_transfer_stop(sc->sc_xfer[UDBP_T_WR]); /* cleanup queues */ NG_BT_MBUFQ_DRAIN(&sc->sc_xmitq); NG_BT_MBUFQ_DRAIN(&sc->sc_xmitq_hipri); if (sc->sc_bulk_in_buffer) { m_freem(sc->sc_bulk_in_buffer); sc->sc_bulk_in_buffer = NULL; } sc->sc_hook = NULL; } mtx_unlock(&(sc->sc_mtx)); } if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) && (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))) ng_rmnode_self(NG_HOOK_NODE(hook)); return error; } pwcbsd/usb/udbp.h000644 000423 000000 00000006630 10551670754 014511 0ustar00luigiwheel000000 000000 /*- * Copyright (c) 1996-2000 Whistle Communications, Inc. * All rights reserved. * * Subject to the following obligations and disclaimer of warranty, use and * redistribution of this software, in source or object code forms, with or * without modifications are expressly permitted by Whistle Communications; * provided, however, that: * 1. Any and all reproductions of the source or object code must include the * copyright notice above and the following disclaimer of warranties; and * 2. No rights are granted, in any manner or form, to use Whistle * Communications, Inc. trademarks, including the mark "WHISTLE * COMMUNICATIONS" on advertising, endorsements, or otherwise except as * such appears in the above copyright notice or in the software. * * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. * * This file was derived from src/sys/netgraph/ng_sample.h, revision 1.1 * written by Julian Elischer, Whistle Communications. * * $FreeBSD: src/sys/dev/usb/udbp.h,v 1.4 2005/01/06 01:43:28 imp Exp $ */ #ifndef _NETGRAPH_UDBP_H_ #define _NETGRAPH_UDBP_H_ /* Node type name. This should be unique among all netgraph node types */ #define NG_UDBP_NODE_TYPE "udbp" /* Node type cookie. Should also be unique. This value MUST change whenever an incompatible change is made to this header file, to insure consistency. The de facto method for generating cookies is to take the output of the date command: date -u +'%s' */ #define NGM_UDBP_COOKIE 944609300 #define NG_UDBP_HOOK_NAME "data" /* Netgraph commands understood by this node type */ enum { NGM_UDBP_SET_FLAG = 1, NGM_UDBP_GET_STATUS, }; /* This structure is returned by the NGM_UDBP_GET_STATUS command */ struct ngudbpstat { uint32_t packets_in; /* packets in from downstream */ uint32_t packets_out; /* packets out towards downstream */ }; /* * This is used to define the 'parse type' for a struct ngudbpstat, which * is bascially a description of how to convert a binary struct ngudbpstat * to an ASCII string and back. See ng_parse.h for more info. * * This needs to be kept in sync with the above structure definition */ #define NG_UDBP_STATS_TYPE_INFO { \ { "packets_in", &ng_parse_int32_type }, \ { "packets_out", &ng_parse_int32_type }, \ { NULL }, \ } #endif /* _NETGRAPH_UDBP_H_ */ pwcbsd/usb/ufm.c000644 000423 000000 00000024247 10551670754 014345 0ustar00luigiwheel000000 000000 /*- * Copyright (c) 2001 M. Warner Losh * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * This code is based on ugen.c and ulpt.c developed by Lennart Augustsson. * This code includes software developed by the NetBSD Foundation, Inc. and * its contributors. */ #include __FBSDID("$FreeBSD: src/sys/dev/usb/ufm.c,v 1.25 2006/09/07 00:06:41 imp Exp $"); #include #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" #include #ifdef USB_DEBUG #define DPRINTF(sc,n,fmt,...) \ do { if (ufm_debug > (n)) { \ printf("%s:%s: " fmt, (sc)->sc_name, \ __FUNCTION__,## __VA_ARGS__); } } while (0) static int ufm_debug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, ufm, CTLFLAG_RW, 0, "USB ufm"); SYSCTL_INT(_hw_usb_ufm, OID_AUTO, debug, CTLFLAG_RW, &ufm_debug, 0, "ufm debug level"); #else #define DPRINTF(...) #endif #define UFM_N_TRANSFER 1 /* units */ #define UFM_BUF_SIZE (sizeof(usb_device_request_t) + 1) /* bytes */ #define UFM_CMD0 0x00 #define UFM_CMD_SET_FREQ 0x01 #define UFM_CMD2 0x02 struct ufm_softc { struct usb_cdev sc_cdev; struct mtx sc_mtx; struct usbd_device *sc_udev; struct usbd_xfer *sc_xfer[UFM_N_TRANSFER]; u_int32_t sc_unit; u_int32_t sc_freq; u_int16_t sc_flags; #define UFM_FLAG_COMMAND_ERR 0x0001 u_int8_t sc_transfer_buf[UFM_BUF_SIZE]; u_int8_t sc_name[16]; }; /* prototypes */ static device_probe_t ufm_probe; static device_attach_t ufm_attach; static device_detach_t ufm_detach; static int32_t ufm_open(struct usb_cdev *dev, int32_t fflags, int32_t devtype, struct thread *td); static void ufm_ioctl_callback(struct usbd_xfer *xfer); static int ufm_do_req(struct ufm_softc *sc, int32_t fflags, u_int8_t request, u_int16_t value, u_int16_t index, u_int8_t *retbuf); static int ufm_set_freq(struct ufm_softc *sc, caddr_t addr, int32_t fflags); static int ufm_get_freq(struct ufm_softc *sc, caddr_t addr, int32_t fflags); static int ufm_start(struct ufm_softc *sc, caddr_t addr, int32_t fflags); static int ufm_stop(struct ufm_softc *sc, caddr_t addr, int32_t fflags); static int ufm_get_stat(struct ufm_softc *sc, caddr_t addr, int32_t fflags); static int ufm_ioctl(struct usb_cdev *dev, u_long cmd, caddr_t addr, int32_t fflags, struct thread *td); static const struct usbd_config ufm_config[UFM_N_TRANSFER] = { [0] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = UFM_BUF_SIZE, .flags = USBD_USE_DMA, .callback = &ufm_ioctl_callback, .timeout = 1000, /* 1 second */ }, }; static devclass_t ufm_devclass; static device_method_t ufm_methods[] = { DEVMETHOD(device_probe, ufm_probe), DEVMETHOD(device_attach, ufm_attach), DEVMETHOD(device_detach, ufm_detach), { 0, 0 } }; static driver_t ufm_driver = { .name = "ufm", .methods = ufm_methods, .size = sizeof(struct ufm_softc), }; MODULE_DEPEND(ufm, usb, 1, 1, 1); DRIVER_MODULE(ufm, uhub, ufm_driver, ufm_devclass, usbd_driver_load, 0); static int ufm_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); if (uaa->iface == NULL) { return UMATCH_NONE; } if ((uaa->vendor == USB_VENDOR_CYPRESS) && (uaa->product == USB_PRODUCT_CYPRESS_FMRADIO)) { return UMATCH_VENDOR_PRODUCT; } return UMATCH_NONE; } static int ufm_attach(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct ufm_softc *sc = device_get_softc(dev); const char * p_buf[2]; char buf[16]; int32_t error; if (sc == NULL) { return ENOMEM; } sc->sc_udev = uaa->device; sc->sc_unit = device_get_unit(dev); snprintf(sc->sc_name, sizeof(sc->sc_name), "%s", device_get_nameunit(dev)); mtx_init(&(sc->sc_mtx), "ufm lock", NULL, MTX_DEF|MTX_RECURSE); usbd_set_desc(dev, uaa->device); error = usbd_transfer_setup(uaa->device, uaa->iface_index, sc->sc_xfer, ufm_config, UFM_N_TRANSFER, sc, &(sc->sc_mtx)); if (error) { DPRINTF(sc, 0, "error=%s\n", usbd_errstr(error)) ; goto detach; } snprintf(buf, sizeof(buf), "ufm%d", sc->sc_unit); p_buf[0] = buf; p_buf[1] = NULL; sc->sc_cdev.sc_open = &ufm_open; sc->sc_cdev.sc_ioctl = &ufm_ioctl; error = usb_cdev_attach(&(sc->sc_cdev), sc, &(sc->sc_mtx), p_buf, UID_ROOT, GID_OPERATOR, 0644, 1, 1, 1, 1); if (error) { goto detach; } return 0; /* success */ detach: ufm_detach(dev); return ENXIO; } static int ufm_detach(device_t dev) { struct ufm_softc *sc = device_get_softc(dev); usb_cdev_detach(&(sc->sc_cdev)); usbd_transfer_unsetup(sc->sc_xfer, UFM_N_TRANSFER); mtx_destroy(&(sc->sc_mtx)); return 0; } static int32_t ufm_open(struct usb_cdev *dev, int32_t fflags, int32_t devtype, struct thread *td) { if ((fflags & (FWRITE|FREAD)) != (FWRITE|FREAD)) { return EACCES; } return 0; } static void ufm_ioctl_callback(struct usbd_xfer *xfer) { struct ufm_softc *sc = xfer->priv_sc; USBD_CHECK_STATUS(xfer); tr_transferred: usbd_copy_out(&(xfer->buf_data), 0, sc->sc_transfer_buf, UFM_BUF_SIZE); sc->sc_flags &= ~UFM_FLAG_COMMAND_ERR; usb_cdev_wakeup(&(sc->sc_cdev)); return; tr_error: DPRINTF(sc, 0, "error=%s\n", usbd_errstr(xfer->error)); sc->sc_flags |= UFM_FLAG_COMMAND_ERR; usb_cdev_wakeup(&(sc->sc_cdev)); return; tr_setup: usbd_copy_in(&(xfer->buf_data), 0, sc->sc_transfer_buf, UFM_BUF_SIZE); xfer->length = UFM_BUF_SIZE; usbd_start_hardware(xfer); return; } static int ufm_do_req(struct ufm_softc *sc, int32_t fflags, u_int8_t request, u_int16_t value, u_int16_t index, u_int8_t *retbuf) { int32_t error; usb_device_request_t *req = (void *)(sc->sc_transfer_buf); req->bmRequestType = UT_READ_VENDOR_DEVICE; req->bRequest = request; USETW(req->wValue, value); USETW(req->wIndex, index); USETW(req->wLength, 1); sc->sc_flags |= UFM_FLAG_COMMAND_ERR; usbd_transfer_start(sc->sc_xfer[0]); error = usb_cdev_sleep(&(sc->sc_cdev), fflags, 0); usbd_transfer_stop(sc->sc_xfer[0]); if (retbuf) { *retbuf = req->bData[0]; } if (error) { return error; } if (sc->sc_flags & UFM_FLAG_COMMAND_ERR) { return ENXIO; } return 0; } static int ufm_set_freq(struct ufm_softc *sc, caddr_t addr, int32_t fflags) { int freq = *(int *)addr; /* * Freq now is in Hz. We need to convert it to the frequency * that the radio wants. This frequency is 10.7MHz above * the actual frequency. We then need to convert to * units of 12.5kHz. We add one to the IFM to make rounding * easier. */ sc->sc_freq = freq; freq = (freq + 10700001) / 12500; /* This appears to set the frequency */ if (ufm_do_req(sc, fflags, UFM_CMD_SET_FREQ, freq >> 8, freq, NULL) != 0) { return EIO; } /* Not sure what this does */ if (ufm_do_req(sc, fflags, UFM_CMD0, 0x96, 0xb7, NULL) != 0) { return EIO; } return (0); } static int ufm_get_freq(struct ufm_softc *sc, caddr_t addr, int32_t fflags) { int *valp = (int *)addr; *valp = sc->sc_freq; return (0); } static int ufm_start(struct ufm_softc *sc, caddr_t addr, int32_t fflags) { u_int8_t ret; if (ufm_do_req(sc, fflags, UFM_CMD0, 0x00, 0xc7, &ret)) { return EIO; } if (ufm_do_req(sc, fflags, UFM_CMD2, 0x01, 0x00, &ret)) { return EIO; } if (ret & 0x1) { return EIO; } return (0); } static int ufm_stop(struct ufm_softc *sc, caddr_t addr, int32_t fflags) { if (ufm_do_req(sc, fflags, UFM_CMD0, 0x16, 0x1C, NULL)) { return EIO; } if (ufm_do_req(sc, fflags, UFM_CMD2, 0x00, 0x00, NULL)) { return EIO; } return (0); } static int ufm_get_stat(struct ufm_softc *sc, caddr_t addr, int32_t fflags) { u_int8_t ret; u_int32_t timeout = (hz / 4); if (timeout == 0) { timeout = 1; } /* * Note, there's a 240ms settle time before the status * will be valid, so sleep that amount. hz/4 is a good * approximation of that. */ if (usb_cdev_sleep(&(sc->sc_cdev), fflags, timeout)) { return EIO; } if (ufm_do_req(sc, fflags, UFM_CMD0, 0x00, 0x24, &ret)) { return EIO; } *(int *)addr = ret; return 0; } static int ufm_ioctl(struct usb_cdev *dev, u_long cmd, caddr_t addr, int32_t fflags, struct thread *td) { struct ufm_softc *sc = dev->sc_priv_ptr; int error = 0; switch (cmd) { case FM_SET_FREQ: error = ufm_set_freq(sc, addr, fflags); break; case FM_GET_FREQ: error = ufm_get_freq(sc, addr, fflags); break; case FM_START: error = ufm_start(sc, addr, fflags); break; case FM_STOP: error = ufm_stop(sc, addr, fflags); break; case FM_GET_STAT: error = ufm_get_stat(sc, addr, fflags); break; default: return ENOTTY; break; } return error; } pwcbsd/usb/ufoma.c000644 000423 000000 00000100704 10551670754 014656 0ustar00luigiwheel000000 000000 /* $NetBSD: umodem.c,v 1.45 2002/09/23 05:51:23 simonb Exp $ */ #include __FBSDID("$FreeBSD: src/sys/dev/usb/ufoma.c,v 1.2 2006/09/07 00:06:41 imp Exp $"); /*- * Copyright (c) 2005, Takanori Watanabe * Copyright (c) 2003, M. Warner Losh . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * Comm Class spec: http://www.usb.org/developers/devclass_docs/usbccs10.pdf * http://www.usb.org/developers/devclass_docs/usbcdc11.pdf */ /* * TODO: * - Implement a Call Device for modems without multiplexed commands. */ /* * NOTE: all function names beginning like "ufoma_cfg_" can only * be called from within the config thread function ! */ #include #include #include #include #include #include #include #define usbd_config_td_cc ufoma_config_copy #define usbd_config_td_softc ufoma_softc #include #include #include #include #include #include #include "usbdevs.h" typedef struct ufoma_mobile_acm_descriptor{ u_int8_t bFunctionLength; u_int8_t bDescriptorType; u_int8_t bDescriptorSubtype; u_int8_t bType; u_int8_t bMode[1]; } UPACKED usb_mcpc_acm_descriptor; #define UISUBCLASS_MCPC 0x88 #define UDESC_VS_INTERFACE 0x44 #define UDESCSUB_MCPC_ACM 0x11 #define UMCPC_ACM_TYPE_AB1 0x1 #define UMCPC_ACM_TYPE_AB2 0x2 #define UMCPC_ACM_TYPE_AB5 0x5 #define UMCPC_ACM_TYPE_AB6 0x6 #define UMCPC_ACM_MODE_DEACTIVATED 0x0 #define UMCPC_ACM_MODE_MODEM 0x1 #define UMCPC_ACM_MODE_ATCOMMAND 0x2 #define UMCPC_ACM_MODE_OBEX 0x60 #define UMCPC_ACM_MODE_VENDOR1 0xc0 #define UMCPC_ACM_MODE_VENDOR2 0xfe #define UMCPC_ACM_MODE_UNLINKED 0xff #define UMCPC_CM_MOBILE_ACM 0x0 #define UMCPC_ACTIVATE_MODE 0x60 #define UMCPC_GET_MODETABLE 0x61 #define UMCPC_SET_LINK 0x62 #define UMCPC_CLEAR_LINK 0x63 #define UMCPC_REQUEST_ACKNOWLEDGE 0x31 #define UFOMA_MAX_TIMEOUT 15 /* standard says 10 seconds */ #define UFOMA_CMD_BUF_SIZE 64 /* bytes */ #define UFOMA_BULK_IBUFSIZE 64 /* bytes */ #define UFOMA_BULK_OBUFSIZE 256 /* bytes */ #define UFOMA_CTRL_ENDPT_MAX 4 /* units */ #define UFOMA_BULK_ENDPT_MAX 4 /* units */ #define DPRINTF(...) struct ufoma_softc { struct ucom_softc sc_ucom; struct usbd_config_td sc_config_td; usb_cdc_line_state_t sc_line_state; /* current line state */ struct usbd_xfer * sc_ctrl_xfer[UFOMA_CTRL_ENDPT_MAX]; struct usbd_xfer * sc_bulk_xfer[UFOMA_BULK_ENDPT_MAX]; u_int8_t * sc_modetable; device_t sc_dev; struct usbd_device * sc_udev; u_int32_t sc_unit; u_int8_t sc_num_msg; u_int8_t sc_is_pseudo; u_int8_t sc_ctrl_iface_no; u_int8_t sc_ctrl_iface_index; u_int8_t sc_data_iface_no; u_int8_t sc_data_iface_index; u_int8_t sc_cm_cap; u_int8_t sc_acm_cap; u_int8_t sc_dtr; /* current DTR state */ u_int8_t sc_rts; /* current RTS state */ u_int8_t sc_lsr; u_int8_t sc_msr; u_int8_t sc_break; u_int8_t sc_modetoactivate; u_int8_t sc_currentmode; u_int8_t sc_flags; #define UFOMA_FLAG_INTR_STALL 0x01 #define UFOMA_FLAG_BULK_WRITE_STALL 0x02 #define UFOMA_FLAG_BULK_READ_STALL 0x04 u_int8_t sc_name[16]; }; struct ufoma_config_copy { usb_cdc_line_state_t line_state; u_int8_t break_onoff; u_int8_t dtr_onoff; u_int8_t rts_onoff; }; /* prototypes */ static device_probe_t ufoma_probe; static device_attach_t ufoma_attach; static device_detach_t ufoma_detach; static void ufoma_cfg_do_request(struct ufoma_softc *sc, usb_device_request_t *req, void *data); static void * ufoma_get_intconf(usb_config_descriptor_t *cd, usb_interface_descriptor_t *id, u_int8_t type, u_int8_t subtype); static void ufoma_cfg_link_state(struct ufoma_softc *sc); static void ufoma_cfg_activate_state(struct ufoma_softc *sc, u_int16_t state); static void ufoma_ctrl_read_callback(struct usbd_xfer *xfer); static void ufoma_ctrl_write_callback(struct usbd_xfer *xfer); static void ufoma_intr_clear_stall_callback(struct usbd_xfer *xfer); static void ufoma_intr_callback(struct usbd_xfer *xfer); static void ufoma_bulk_write_callback(struct usbd_xfer *xfer); static void ufoma_bulk_write_clear_stall_callback(struct usbd_xfer *xfer); static void ufoma_bulk_read_callback(struct usbd_xfer *xfer); static void ufoma_bulk_read_clear_stall_callback(struct usbd_xfer *xfer); static void ufoma_config_copy(struct ufoma_softc *sc, struct ufoma_config_copy *cc, u_int16_t refcount); static int ufoma_open(struct ucom_softc *ucom); static void ufoma_cfg_open(struct ufoma_softc *sc, struct ufoma_config_copy *cc, u_int16_t refcount); static void ufoma_close(struct ucom_softc *ucom); static void ufoma_cfg_close(struct ufoma_softc *sc, struct ufoma_config_copy *cc, u_int16_t refcount); static void ufoma_set_break(struct ucom_softc *ucom, u_int8_t onoff); static void ufoma_cfg_set_break(struct ufoma_softc *sc, struct ufoma_config_copy *cc, u_int16_t refcount); static void ufoma_get_status(struct ucom_softc *ucom, u_int8_t *lsr, u_int8_t *msr); static void ufoma_cfg_set_line_state(struct ufoma_softc *sc, struct ufoma_config_copy *cc, u_int16_t refcount); static void ufoma_set_dtr(struct ucom_softc *ucom, u_int8_t onoff); static void ufoma_set_rts(struct ucom_softc *ucom, u_int8_t onoff); static void ufoma_cfg_set_line_coding(struct ufoma_softc *sc, struct ufoma_config_copy *cc, u_int16_t refcount); static int ufoma_param(struct ucom_softc *ucom, struct termios *t); static int ufoma_modem_setup(device_t dev, struct ufoma_softc *sc, struct usb_attach_arg *uaa); static void ufoma_start_read(struct ucom_softc *ucom); static void ufoma_stop_read(struct ucom_softc *ucom); static void ufoma_start_write(struct ucom_softc *ucom); static void ufoma_stop_write(struct ucom_softc *ucom); static const struct usbd_config ufoma_ctrl_config[UFOMA_CTRL_ENDPT_MAX] = { [0] = { .type = UE_INTERRUPT, .endpoint = -1, /* any */ .direction = UE_DIR_IN, .flags = USBD_SHORT_XFER_OK, .bufsize = sizeof(usb_cdc_notification_t), .callback = &ufoma_intr_callback, }, [1] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .flags = USBD_USE_DMA, .callback = &ufoma_intr_clear_stall_callback, .timeout = 1000, /* 1 second */ }, [2] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = (sizeof(usb_device_request_t) + UFOMA_CMD_BUF_SIZE), .flags = USBD_SHORT_XFER_OK, .callback = &ufoma_ctrl_read_callback, .timeout = 1000, /* 1 second */ }, [3] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = (sizeof(usb_device_request_t) + 1), .flags = 0, .callback = &ufoma_ctrl_write_callback, .timeout = 1000, /* 1 second */ }, }; static const struct usbd_config ufoma_bulk_config[UFOMA_BULK_ENDPT_MAX] = { [0] = { .type = UE_BULK, .endpoint = -1, /* any */ .direction = UE_DIR_OUT, .bufsize = UFOMA_BULK_OBUFSIZE, .flags = 0, .callback = &ufoma_bulk_write_callback, }, [1] = { .type = UE_BULK, .endpoint = -1, /* any */ .direction = UE_DIR_IN, .bufsize = UFOMA_BULK_IBUFSIZE, .flags = USBD_SHORT_XFER_OK, .callback = &ufoma_bulk_read_callback, }, [2] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .flags = (USBD_USE_DMA), .callback = &ufoma_bulk_write_clear_stall_callback, .timeout = 1000, /* 1 second */ }, [3] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .flags = (USBD_USE_DMA), .callback = &ufoma_bulk_read_clear_stall_callback, .timeout = 1000, /* 1 second */ }, }; static const struct ucom_callback ufoma_callback = { .ucom_get_status = &ufoma_get_status, .ucom_set_dtr = &ufoma_set_dtr, .ucom_set_rts = &ufoma_set_rts, .ucom_set_break = &ufoma_set_break, .ucom_param = &ufoma_param, .ucom_open = &ufoma_open, .ucom_close = &ufoma_close, .ucom_start_read = &ufoma_start_read, .ucom_stop_read = &ufoma_stop_read, .ucom_start_write = &ufoma_start_write, .ucom_stop_write = &ufoma_stop_write, }; static device_method_t ufoma_methods[] = { /* Device methods */ DEVMETHOD(device_probe, ufoma_probe), DEVMETHOD(device_attach, ufoma_attach), DEVMETHOD(device_detach, ufoma_detach), { 0, 0 } }; static devclass_t ufoma_devclass; static driver_t ufoma_driver = { .name = "ufoma", .methods = ufoma_methods, .size = sizeof(struct ufoma_softc), }; DRIVER_MODULE(ufoma, uhub, ufoma_driver, ufoma_devclass, usbd_driver_load, 0); MODULE_DEPEND(ufoma, usb, 1, 1, 1); MODULE_DEPEND(ufoma, ucom, UCOM_MINVER, UCOM_PREFVER, UCOM_MAXVER); static int ufoma_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); usb_interface_descriptor_t *id; usb_config_descriptor_t *cd; usb_mcpc_acm_descriptor *mad; if(uaa->iface == NULL) { return UMATCH_NONE; } id = usbd_get_interface_descriptor(uaa->iface); cd = usbd_get_config_descriptor(uaa->device); if ((id == NULL) || (cd == NULL) || (id->bInterfaceClass != UICLASS_CDC) || (id->bInterfaceSubClass != UISUBCLASS_MCPC)){ return UMATCH_NONE; } mad = ufoma_get_intconf(cd, id, UDESC_VS_INTERFACE, UDESCSUB_MCPC_ACM); if(mad == NULL){ return UMATCH_NONE; } #if 0 if(mad->bType != UMCPC_ACM_TYPE_AB5){ return UMATCH_NONE; } #endif return UMATCH_IFACECLASS_IFACESUBCLASS; } static int ufoma_attach(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct ufoma_softc *sc = device_get_softc(dev); usb_config_descriptor_t *cd; usb_interface_descriptor_t *id; usb_mcpc_acm_descriptor *mad; u_int8_t elements; int32_t error; if (sc == NULL) { return ENOMEM; } sc->sc_udev = uaa->device; sc->sc_dev = dev; sc->sc_unit = device_get_unit(dev); usbd_set_desc(dev, uaa->device); snprintf(sc->sc_name, sizeof(sc->sc_name), "%s", device_get_nameunit(dev)); DPRINTF(sc, 0, "\n"); /* setup control transfers */ cd = usbd_get_config_descriptor(uaa->device); id = usbd_get_interface_descriptor(uaa->iface); sc->sc_ctrl_iface_no = id->bInterfaceNumber; sc->sc_ctrl_iface_index = uaa->iface_index; error = usbd_transfer_setup (uaa->device, sc->sc_ctrl_iface_index, sc->sc_ctrl_xfer, ufoma_ctrl_config, UFOMA_CTRL_ENDPT_MAX, sc, &Giant); if (error) { device_printf(dev, "allocating control USB " "transfers failed!\n"); goto detach; } mad = ufoma_get_intconf(cd, id, UDESC_VS_INTERFACE, UDESCSUB_MCPC_ACM); if (mad == NULL){ goto detach; } if (mad->bFunctionLength < sizeof(*mad)) { device_printf(dev, "invalid MAD descriptor\n"); goto detach; } if(mad->bType == UMCPC_ACM_TYPE_AB5) { sc->sc_is_pseudo = 1; } else { sc->sc_is_pseudo = 0; if (ufoma_modem_setup(dev, sc, uaa)) { goto detach; } } elements = (mad->bFunctionLength - sizeof(*mad) + 1); /* initialize mode variables */ sc->sc_modetable = malloc(elements + 1, M_USBDEV, M_WAITOK); if (sc->sc_modetable == NULL) { goto detach; } sc->sc_modetable[0] = (elements + 1); bcopy(mad->bMode, &(sc->sc_modetable[1]), elements); sc->sc_currentmode = UMCPC_ACM_MODE_UNLINKED; sc->sc_modetoactivate = mad->bMode[0]; error = ucom_attach(&(sc->sc_ucom), 1, sc, &ufoma_callback, &Giant); if (error) { DPRINTF(0, "ucom_attach failed\n"); goto detach; } /* setup config thread */ error = usbd_config_td_setup(&(sc->sc_config_td), sc, &Giant, &ufoma_config_copy, NULL, sizeof(struct ufoma_config_copy), 16); if (error) { device_printf(dev, "could not setup config " "thread!\n"); goto detach; } return 0; /* success */ detach: ufoma_detach(dev); return ENXIO; /* failure */ } static int ufoma_detach(device_t dev) { struct ufoma_softc *sc = device_get_softc(dev); mtx_lock(&Giant); usbd_config_td_stop(&(sc->sc_config_td)); mtx_unlock(&Giant); ucom_detach(&(sc->sc_ucom), 1); usbd_transfer_unsetup(sc->sc_ctrl_xfer, UFOMA_CTRL_ENDPT_MAX); usbd_transfer_unsetup(sc->sc_bulk_xfer, UFOMA_BULK_ENDPT_MAX); usbd_config_td_unsetup(&(sc->sc_config_td)); if (sc->sc_modetable) { free(sc->sc_modetable, M_USBDEV); } return 0; } static void ufoma_cfg_do_request(struct ufoma_softc *sc, usb_device_request_t *req, void *data) { u_int16_t length; usbd_status err; if (usbd_config_td_is_gone(&(sc->sc_config_td))) { goto error; } err = usbd_do_request_flags_mtx(sc->sc_udev, &Giant, req, data, 0, NULL, 1000); if (err) { DPRINTF(sc, 0, "device request failed, err=%s " "(ignored)\n", usbd_errstr(err)); error: length = UGETW(req->wLength); if ((req->bmRequestType & UT_READ) && length) { bzero(data, length); } } return; } static void * ufoma_get_intconf(usb_config_descriptor_t *cd, usb_interface_descriptor_t *id, u_int8_t type, u_int8_t subtype) { usb_descriptor_t *desc = (void *)id; while ((desc = usbd_desc_foreach(cd,desc))) { if(desc->bDescriptorType == UDESC_INTERFACE){ return NULL; } if((desc->bDescriptorType == type) && (desc->bDescriptorSubtype == subtype)) { break; } } return desc; } static void ufoma_cfg_link_state(struct ufoma_softc *sc) { usb_device_request_t req; int32_t error; req.bmRequestType = UT_WRITE_VENDOR_INTERFACE; req.bRequest = UMCPC_SET_LINK; USETW(req.wValue, UMCPC_CM_MOBILE_ACM); USETW(req.wIndex, sc->sc_ctrl_iface_no); USETW(req.wLength, sc->sc_modetable[0]); ufoma_cfg_do_request(sc, &req, sc->sc_modetable); error = msleep(&(sc->sc_currentmode), &Giant, PZERO, "ufoma_link", hz); if(error){ DPRINTF(sc, 0, "NO response\n"); } return; } static void ufoma_cfg_activate_state(struct ufoma_softc *sc, u_int16_t state) { usb_device_request_t req; int32_t error; req.bmRequestType = UT_WRITE_VENDOR_INTERFACE; req.bRequest = UMCPC_ACTIVATE_MODE; USETW(req.wValue, state); USETW(req.wIndex, sc->sc_ctrl_iface_no); USETW(req.wLength, 0); ufoma_cfg_do_request(sc, &req, NULL); error = msleep(&(sc->sc_currentmode), &Giant, PZERO, "fmaact", (UFOMA_MAX_TIMEOUT*hz)); if(error){ DPRINTF(sc, 0, "No response\n"); } return; } static void ufoma_ctrl_read_callback(struct usbd_xfer *xfer) { struct ufoma_softc *sc = xfer->priv_sc; usb_device_request_t *req = xfer->buffer; USBD_CHECK_STATUS(xfer); tr_error: DPRINTF(sc, 0, "error = %s\n", usbd_errstr(xfer->error)); if (xfer->error == USBD_CANCELLED) { return; } else { goto tr_setup; } tr_transferred: if (xfer->actlen < sizeof(*req)) { goto tr_setup; } xfer->actlen -= sizeof(*req); if (xfer->actlen) { ucom_put_data(&(sc->sc_ucom), req->bData, xfer->actlen); } tr_setup: if (sc->sc_num_msg) { sc->sc_num_msg--; req->bmRequestType = UT_READ_CLASS_INTERFACE; req->bRequest = UCDC_GET_ENCAPSULATED_RESPONSE; USETW(req->wIndex, sc->sc_ctrl_iface_no); USETW(req->wValue, 0); USETW(req->wLength, UFOMA_CMD_BUF_SIZE); usbd_start_hardware(xfer); } return; } static void ufoma_ctrl_write_callback(struct usbd_xfer *xfer) { struct ufoma_softc *sc = xfer->priv_sc; usb_device_request_t *req = xfer->buffer; u_int32_t actlen; USBD_CHECK_STATUS(xfer); tr_error: DPRINTF(sc, 0, "error = %s\n", usbd_errstr(xfer->error)); if (xfer->error == USBD_CANCELLED) { return; } else { goto tr_setup; } tr_transferred: tr_setup: if (ucom_get_data(&(sc->sc_ucom), req->bData, 1, &actlen)) { req->bmRequestType = UT_WRITE_CLASS_INTERFACE; req->bRequest = UCDC_SEND_ENCAPSULATED_COMMAND; USETW(req->wIndex, sc->sc_ctrl_iface_no); USETW(req->wValue, 0); USETW(req->wLength, 1); xfer->length = (sizeof(*req) + 1); usbd_start_hardware(xfer); } return; } static void ufoma_intr_clear_stall_callback(struct usbd_xfer *xfer) { struct ufoma_softc *sc = xfer->priv_sc; struct usbd_xfer *xfer_other = sc->sc_ctrl_xfer[0]; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, xfer_other); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, xfer_other); sc->sc_flags &= ~UFOMA_FLAG_INTR_STALL; usbd_transfer_start(xfer_other); return; tr_error: /* bomb out */ sc->sc_flags &= ~UFOMA_FLAG_INTR_STALL; DPRINTF(sc, 0, "interrupt read pipe stopped\n"); return; } static void ufoma_intr_callback(struct usbd_xfer *xfer) { struct ufoma_softc *sc = xfer->priv_sc; usb_cdc_notification_t *n = xfer->buffer; u_int16_t wLen; u_int16_t temp; u_int8_t mstatus; USBD_CHECK_STATUS(xfer); tr_error: if (xfer->error != USBD_CANCELLED) { /* start clear stall */ sc->sc_flags |= UFOMA_FLAG_INTR_STALL; usbd_transfer_start(sc->sc_ctrl_xfer[1]); } return; tr_transferred: if (xfer->actlen < 8) { DPRINTF(sc, 0, "too short message\n"); goto tr_setup; } xfer->actlen -= 8; wLen = UGETW(n->wLength); xfer->actlen = min(wLen, xfer->actlen); if((n->bmRequestType == UT_READ_VENDOR_INTERFACE) && (n->bNotification == UMCPC_REQUEST_ACKNOWLEDGE)) { temp = UGETW(n->wValue); sc->sc_currentmode = (temp >> 8); if(!(temp & 0xff)){ DPRINTF(sc, 0, "Mode change failed!\n"); } wakeup(&(sc->sc_currentmode)); } if(n->bmRequestType != UCDC_NOTIFICATION){ goto tr_setup; } switch(n->bNotification){ case UCDC_N_RESPONSE_AVAILABLE: if(!(sc->sc_is_pseudo)){ DPRINTF(sc, 0, "Wrong serial state!\n"); break; } if (sc->sc_num_msg != 0xFF) { sc->sc_num_msg ++; } usbd_transfer_start(sc->sc_ctrl_xfer[3]); break; case UCDC_N_SERIAL_STATE: if(sc->sc_is_pseudo){ DPRINTF(sc, 0, "Wrong serial state!\n"); break; } /* * Set the serial state in ucom driver based on * the bits from the notify message */ if (xfer->actlen < 2) { DPRINTF(sc, 0, "invalid notification " "length, %d bytes!\n", xfer->actlen); break; } DPRINTF(sc, 0, "notify bytes = 0x%02x, 0x%02x\n", n->data[0], n->data[1]); /* currently, lsr is always zero. */ sc->sc_lsr = 0; sc->sc_msr = 0; mstatus = n->data[0]; if (mstatus & UCDC_N_SERIAL_RI) { sc->sc_msr |= SER_RI; } if (mstatus & UCDC_N_SERIAL_DSR) { sc->sc_msr |= SER_DSR; } if (mstatus & UCDC_N_SERIAL_DCD) { sc->sc_msr |= SER_DCD; } ucom_status_change(&(sc->sc_ucom)); break; default: break; } tr_setup: if (sc->sc_flags & UFOMA_FLAG_INTR_STALL) { usbd_transfer_start(sc->sc_ctrl_xfer[1]); } else { usbd_start_hardware(xfer); } return; } static void ufoma_bulk_write_callback(struct usbd_xfer *xfer) { struct ufoma_softc *sc = xfer->priv_sc; u_int32_t actlen; USBD_CHECK_STATUS(xfer); tr_error: if (xfer->error != USBD_CANCELLED) { sc->sc_flags |= UFOMA_FLAG_BULK_WRITE_STALL; usbd_transfer_start(sc->sc_bulk_xfer[2]); } return; tr_setup: tr_transferred: if (sc->sc_flags & UFOMA_FLAG_BULK_WRITE_STALL) { usbd_transfer_start(sc->sc_bulk_xfer[2]); return; } if (ucom_get_data(&(sc->sc_ucom), xfer->buffer, UFOMA_BULK_OBUFSIZE, &actlen)) { xfer->length = actlen; usbd_start_hardware(xfer); } return; } static void ufoma_bulk_write_clear_stall_callback(struct usbd_xfer *xfer) { struct ufoma_softc *sc = xfer->priv_sc; struct usbd_xfer *xfer_other = sc->sc_bulk_xfer[0]; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, xfer_other); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, xfer_other); sc->sc_flags &= ~UFOMA_FLAG_BULK_WRITE_STALL; usbd_transfer_start(xfer_other); return; tr_error: sc->sc_flags &= ~UFOMA_FLAG_BULK_WRITE_STALL; DPRINTF(sc, 0, "clear stall failed, error=%s\n", usbd_errstr(xfer->error)); return; } static void ufoma_bulk_read_callback(struct usbd_xfer *xfer) { struct ufoma_softc *sc = xfer->priv_sc; USBD_CHECK_STATUS(xfer); tr_error: if (xfer->error != USBD_CANCELLED) { sc->sc_flags |= UFOMA_FLAG_BULK_READ_STALL; usbd_transfer_start(sc->sc_bulk_xfer[3]); } return; tr_transferred: ucom_put_data(&(sc->sc_ucom), xfer->buffer, xfer->actlen); tr_setup: if (sc->sc_flags & UFOMA_FLAG_BULK_READ_STALL) { usbd_transfer_start(sc->sc_bulk_xfer[3]); } else { usbd_start_hardware(xfer); } return; } static void ufoma_bulk_read_clear_stall_callback(struct usbd_xfer *xfer) { struct ufoma_softc *sc = xfer->priv_sc; struct usbd_xfer *xfer_other = sc->sc_bulk_xfer[1]; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, xfer_other); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, xfer_other); sc->sc_flags &= ~UFOMA_FLAG_BULK_READ_STALL; usbd_transfer_start(xfer_other); return; tr_error: sc->sc_flags &= ~UFOMA_FLAG_BULK_READ_STALL; DPRINTF(sc, 0, "clear stall failed, error=%s\n", usbd_errstr(xfer->error)); return; } static void ufoma_config_copy(struct ufoma_softc *sc, struct ufoma_config_copy *cc, u_int16_t refcount) { cc->break_onoff = sc->sc_break; cc->dtr_onoff = sc->sc_dtr; cc->rts_onoff = sc->sc_rts; cc->line_state = sc->sc_line_state; return; } static int ufoma_open(struct ucom_softc *ucom) { struct ufoma_softc *sc = ucom->sc_parent; /* start interrupt transfer */ usbd_transfer_start(sc->sc_ctrl_xfer[0]); if (sc->sc_is_pseudo) { /* empty input queue */ if (sc->sc_num_msg != 0xFF) { sc->sc_num_msg ++; } } sc->sc_flags |= (UFOMA_FLAG_BULK_WRITE_STALL| UFOMA_FLAG_BULK_READ_STALL); usbd_config_td_queue_command (&(sc->sc_config_td), &ufoma_cfg_open, 0); return 0; } static void ufoma_cfg_open(struct ufoma_softc *sc, struct ufoma_config_copy *cc, u_int16_t refcount) { if (cc == NULL) { /* nothing to do */ return; } if(sc->sc_currentmode == UMCPC_ACM_MODE_UNLINKED){ ufoma_cfg_link_state(sc); } if(sc->sc_currentmode == UMCPC_ACM_MODE_DEACTIVATED){ ufoma_cfg_activate_state(sc, sc->sc_modetoactivate); } return; } static void ufoma_close(struct ucom_softc *ucom) { struct ufoma_softc *sc = ucom->sc_parent; /* stop interrupt transfer */ usbd_transfer_stop(sc->sc_ctrl_xfer[1]); usbd_transfer_stop(sc->sc_ctrl_xfer[0]); usbd_config_td_queue_command (&(sc->sc_config_td), &ufoma_cfg_close, 0); return; } static void ufoma_cfg_close(struct ufoma_softc *sc, struct ufoma_config_copy *cc, u_int16_t refcount) { if (cc == NULL) { /* nothing to do */ return; } ufoma_cfg_activate_state(sc, UMCPC_ACM_MODE_DEACTIVATED); return; } static void ufoma_set_break(struct ucom_softc *ucom, u_int8_t onoff) { struct ufoma_softc *sc = ucom->sc_parent; sc->sc_break = onoff; if (sc->sc_is_pseudo) { return; } if (!(sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK)) { return; } usbd_config_td_queue_command (&(sc->sc_config_td), &ufoma_cfg_set_break, 0); return; } static void ufoma_cfg_set_break(struct ufoma_softc *sc, struct ufoma_config_copy *cc, u_int16_t refcount) { usb_device_request_t req; if (cc == NULL) { /* nothing to do */ return; } req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UCDC_SEND_BREAK; USETW(req.wValue, cc->break_onoff ? UCDC_BREAK_ON : UCDC_BREAK_OFF); USETW(req.wIndex, sc->sc_ctrl_iface_no); USETW(req.wLength, 0); ufoma_cfg_do_request(sc, &req, 0); return; } static void ufoma_get_status(struct ucom_softc *ucom, u_int8_t *lsr, u_int8_t *msr) { struct ufoma_softc *sc = ucom->sc_parent; if (lsr != NULL) { *lsr = sc->sc_lsr; } if (msr != NULL) { *msr = sc->sc_msr; } return; } static void ufoma_cfg_set_line_state(struct ufoma_softc *sc, struct ufoma_config_copy *cc, u_int16_t refcount) { usb_device_request_t req; u_int16_t ls; if (cc == NULL) { /* nothing to do */ return; } ls = (cc->dtr_onoff ? UCDC_LINE_DTR : 0) | (cc->rts_onoff ? UCDC_LINE_RTS : 0); req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UCDC_SET_CONTROL_LINE_STATE; USETW(req.wValue, ls); USETW(req.wIndex, sc->sc_ctrl_iface_no); USETW(req.wLength, 0); ufoma_cfg_do_request(sc, &req, 0); return; } static void ufoma_set_dtr(struct ucom_softc *ucom, u_int8_t onoff) { struct ufoma_softc *sc = ucom->sc_parent; sc->sc_dtr = onoff; if (sc->sc_is_pseudo) { return; } usbd_config_td_queue_command (&(sc->sc_config_td), &ufoma_cfg_set_line_state, 0); return; } static void ufoma_set_rts(struct ucom_softc *ucom, u_int8_t onoff) { struct ufoma_softc *sc = ucom->sc_parent; sc->sc_rts = onoff; if (sc->sc_is_pseudo) { return; } usbd_config_td_queue_command (&(sc->sc_config_td), &ufoma_cfg_set_line_state, 0); return; } static void ufoma_cfg_set_line_coding(struct ufoma_softc *sc, struct ufoma_config_copy *cc, u_int16_t refcount) { usb_device_request_t req; if (cc == NULL) { /* nothing to do */ return; } req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UCDC_SET_LINE_CODING; USETW(req.wValue, 0); USETW(req.wIndex, sc->sc_ctrl_iface_no); USETW(req.wLength, UCDC_LINE_STATE_LENGTH); ufoma_cfg_do_request(sc, &req, &(cc->line_state)); return; } static int ufoma_param(struct ucom_softc *ucom, struct termios *t) { struct ufoma_softc *sc = ucom->sc_parent; DPRINTF(sc, 0, "\n"); USETDW(sc->sc_line_state.dwDTERate, t->c_ospeed); if (t->c_cflag & CSTOPB) { sc->sc_line_state.bCharFormat = UCDC_STOP_BIT_2; } else { sc->sc_line_state.bCharFormat = UCDC_STOP_BIT_1; } if (t->c_cflag & PARENB) { if (t->c_cflag & PARODD) { sc->sc_line_state.bParityType = UCDC_PARITY_ODD; } else { sc->sc_line_state.bParityType = UCDC_PARITY_EVEN; } } else { sc->sc_line_state.bParityType = UCDC_PARITY_NONE; } switch (t->c_cflag & CSIZE) { case CS5: sc->sc_line_state.bDataBits = 5; break; case CS6: sc->sc_line_state.bDataBits = 6; break; case CS7: sc->sc_line_state.bDataBits = 7; break; case CS8: sc->sc_line_state.bDataBits = 8; break; } if (sc->sc_is_pseudo) { return 0; } usbd_config_td_queue_command (&(sc->sc_config_td), &ufoma_cfg_set_line_coding, 0); return 0; } static int ufoma_modem_setup(device_t dev, struct ufoma_softc *sc, struct usb_attach_arg *uaa) { usb_config_descriptor_t *cd; usb_cdc_acm_descriptor_t *acm; usb_cdc_cm_descriptor_t *cmd; usb_interface_descriptor_t *id; struct usbd_interface *iface; u_int8_t i; int32_t error; cd = usbd_get_config_descriptor(uaa->device); id = usbd_get_interface_descriptor(uaa->iface); cmd = ufoma_get_intconf(cd, id, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM); if ((cmd == NULL) || (cmd->bLength < sizeof(*cmd))) { return EINVAL; } sc->sc_cm_cap = cmd->bmCapabilities; sc->sc_data_iface_no = cmd->bDataInterface; acm = ufoma_get_intconf(cd, id, UDESC_CS_INTERFACE, UDESCSUB_CDC_ACM); if ((acm == NULL) || (acm->bLength < sizeof(*acm))) { return EINVAL; } sc->sc_acm_cap = acm->bmCapabilities; device_printf(dev, "data interface %d, has %sCM over data, " "has %sbreak\n", sc->sc_data_iface_no, sc->sc_cm_cap & USB_CDC_CM_OVER_DATA ? "" : "no ", sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK ? "" : "no "); /* get the data interface too */ for (i = 0; ; i++) { iface = usbd_get_iface(uaa->device, i); if (iface) { id = usbd_get_interface_descriptor(iface); if (id && (id->bInterfaceNumber == sc->sc_data_iface_no)) { sc->sc_data_iface_index = i; USBD_SET_IFACE_NO_PROBE(uaa->device, i); break; } } else { device_printf(dev, "no data interface!\n"); return EINVAL; } } error = usbd_transfer_setup (uaa->device, sc->sc_data_iface_index, sc->sc_bulk_xfer, ufoma_bulk_config, UFOMA_BULK_ENDPT_MAX, sc, &Giant); if (error) { device_printf(dev, "allocating BULK USB " "transfers failed!\n"); return EINVAL; } return 0; } static void ufoma_start_read(struct ucom_softc *ucom) { struct ufoma_softc *sc = ucom->sc_parent; if (sc->sc_is_pseudo) { usbd_transfer_start(sc->sc_ctrl_xfer[2]); } else { usbd_transfer_start(sc->sc_bulk_xfer[1]); } return; } static void ufoma_stop_read(struct ucom_softc *ucom) { struct ufoma_softc *sc = ucom->sc_parent; if (sc->sc_is_pseudo) { usbd_transfer_stop(sc->sc_ctrl_xfer[2]); } else { usbd_transfer_stop(sc->sc_bulk_xfer[3]); usbd_transfer_stop(sc->sc_bulk_xfer[1]); } return; } static void ufoma_start_write(struct ucom_softc *ucom) { struct ufoma_softc *sc = ucom->sc_parent; if (sc->sc_is_pseudo) { usbd_transfer_start(sc->sc_ctrl_xfer[3]); } else { usbd_transfer_start(sc->sc_bulk_xfer[0]); } return; } static void ufoma_stop_write(struct ucom_softc *ucom) { struct ufoma_softc *sc = ucom->sc_parent; if (sc->sc_is_pseudo) { usbd_transfer_stop(sc->sc_ctrl_xfer[3]); } else { usbd_transfer_stop(sc->sc_bulk_xfer[2]); usbd_transfer_stop(sc->sc_bulk_xfer[0]); } return; } pwcbsd/usb/uftdi.c000644 000423 000000 00000062457 10551670754 014676 0ustar00luigiwheel000000 000000 /* $NetBSD: uftdi.c,v 1.13 2002/09/23 05:51:23 simonb Exp $ */ /*- * Copyright (c) 2000 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net). * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD: src/sys/dev/usb/uftdi.c,v 1.24 2006/09/07 00:06:41 imp Exp $"); /* * NOTE: all function names beginning like "uftdi_cfg_" can only * be called from within the config thread function ! */ /* * FTDI FT8U100AX serial adapter driver */ #include #include #include #include #include #include #include #define usbd_config_td_cc uftdi_config_copy #define usbd_config_td_softc uftdi_softc #include #include #include #include #include #include #include #include "usbdevs.h" #ifdef USB_DEBUG #define DPRINTF(sc,n,fmt,...) \ do { if (uftdi_debug > (n)) { \ printf("%s: %s: " fmt, (sc)->sc_name, \ __FUNCTION__,## __VA_ARGS__); } } while (0) static int uftdi_debug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, uftdi, CTLFLAG_RW, 0, "USB uftdi"); SYSCTL_INT(_hw_usb_uftdi, OID_AUTO, debug, CTLFLAG_RW, &uftdi_debug, 0, "uftdi debug level"); #else #define DPRINTF(...) #endif #define UFTDI_CONFIG_INDEX 0 #define UFTDI_IFACE_INDEX 0 #define UFTDI_ENDPT_MAX 4 /* * These are the maximum number of bytes transferred per frame. * The output buffer size cannot be increased due to the size encoding. */ #define UFTDI_IBUFSIZE 64 #define UFTDI_OBUFSIZE 64 struct uftdi_softc { struct ucom_softc sc_ucom; struct usbd_config_td sc_config_td; struct usbd_device * sc_udev; struct usbd_xfer * sc_xfer[UFTDI_ENDPT_MAX]; device_t sc_dev; u_int32_t sc_unit; enum uftdi_type sc_type; u_int16_t sc_last_lcr; u_int16_t sc_rate; u_int8_t sc_iface_index; u_int8_t sc_hdrlen; u_int8_t sc_msr; u_int8_t sc_lsr; u_int8_t sc_dtr_onoff; u_int8_t sc_rts_onoff; u_int8_t sc_break_onoff; u_int8_t sc_flow; u_int8_t sc_flow_v_start; u_int8_t sc_flow_v_stop; u_int8_t sc_flag; #define UFTDI_FLAG_WRITE_STALL 0x01 #define UFTDI_FLAG_READ_STALL 0x02 u_int8_t sc_name[16]; }; struct uftdi_config_copy { u_int16_t last_lcr; u_int16_t rate; u_int8_t dtr_onoff; u_int8_t rts_onoff; u_int8_t break_onoff; u_int8_t flow; u_int8_t v_start; u_int8_t v_stop; }; /* prototypes */ static device_probe_t uftdi_probe; static device_attach_t uftdi_attach; static device_detach_t uftdi_detach; static void uftdi_config_copy(struct uftdi_softc *sc, struct uftdi_config_copy *cc, u_int16_t refcount); static void uftdi_cfg_do_request(struct uftdi_softc *sc, usb_device_request_t *req, void *data); static int uftdi_open(struct ucom_softc *ucom); static void uftdi_cfg_open(struct uftdi_softc *sc, struct uftdi_config_copy *cc, u_int16_t refcount); static void uftdi_write_callback(struct usbd_xfer *xfer); static void uftdi_write_clear_stall_callback(struct usbd_xfer *xfer); static void uftdi_read_callback(struct usbd_xfer *xfer); static void uftdi_read_clear_stall_callback(struct usbd_xfer *xfer); static void uftdi_set_dtr(struct ucom_softc *ucom, u_int8_t onoff); static void uftdi_cfg_set_dtr(struct uftdi_softc *sc, struct uftdi_config_copy *cc, u_int16_t refcount); static void uftdi_set_rts(struct ucom_softc *ucom, u_int8_t onoff); static void uftdi_cfg_set_rts(struct uftdi_softc *sc, struct uftdi_config_copy *cc, u_int16_t refcount); static void uftdi_set_break(struct ucom_softc *ucom, u_int8_t onoff); static void uftdi_cfg_set_break(struct uftdi_softc *sc, struct uftdi_config_copy *cc, u_int16_t refcount); static int uftdi_set_parm_soft(struct uftdi_softc *sc, u_int32_t ospeed, u_int8_t v_start, u_int8_t v_stop, u_int32_t cflag, u_int32_t iflag); static int uftdi_param(struct ucom_softc *ucom, struct termios *t); static void uftdi_cfg_parm(struct uftdi_softc *sc, struct uftdi_config_copy *cc, u_int16_t refcount); static void uftdi_get_status(struct ucom_softc *ucom, u_int8_t *lsr, u_int8_t *msr); static void uftdi_start_read(struct ucom_softc *ucom); static void uftdi_stop_read(struct ucom_softc *ucom); static void uftdi_start_write(struct ucom_softc *ucom); static void uftdi_stop_write(struct ucom_softc *ucom); static const struct usbd_config uftdi_config[UFTDI_ENDPT_MAX] = { [0] = { .type = UE_BULK, .endpoint = -1, /* any */ .direction = UE_DIR_OUT, .bufsize = UFTDI_OBUFSIZE, .flags = 0, .callback = &uftdi_write_callback, }, [1] = { .type = UE_BULK, .endpoint = -1, /* any */ .direction = UE_DIR_IN, .bufsize = UFTDI_IBUFSIZE, .flags = USBD_SHORT_XFER_OK, .callback = &uftdi_read_callback, }, [2] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .flags = (USBD_USE_DMA), .callback = &uftdi_write_clear_stall_callback, .timeout = 1000, /* 1 second */ }, [3] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .flags = (USBD_USE_DMA), .callback = &uftdi_read_clear_stall_callback, .timeout = 1000, /* 1 second */ }, }; static const struct ucom_callback uftdi_callback = { .ucom_get_status = &uftdi_get_status, .ucom_set_dtr = &uftdi_set_dtr, .ucom_set_rts = &uftdi_set_rts, .ucom_set_break = &uftdi_set_break, .ucom_param = &uftdi_param, .ucom_open = &uftdi_open, .ucom_start_read = &uftdi_start_read, .ucom_stop_read = &uftdi_stop_read, .ucom_start_write = &uftdi_start_write, .ucom_stop_write = &uftdi_stop_write, }; static device_method_t uftdi_methods[] = { /* Device interface */ DEVMETHOD(device_probe, uftdi_probe), DEVMETHOD(device_attach, uftdi_attach), DEVMETHOD(device_detach, uftdi_detach), { 0, 0 } }; static devclass_t uftdi_devclass; static driver_t uftdi_driver = { .name = "uftdi", .methods = uftdi_methods, .size = sizeof (struct uftdi_softc), }; DRIVER_MODULE(uftdi, uhub, uftdi_driver, uftdi_devclass, usbd_driver_load, 0); MODULE_DEPEND(uftdi, usb, 1, 1, 1); MODULE_DEPEND(uftdi, ucom, UCOM_MINVER, UCOM_PREFVER, UCOM_MAXVER); static int uftdi_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); if (uaa->iface != NULL) { if ((uaa->vendor == USB_VENDOR_FTDI) && (uaa->product == USB_PRODUCT_FTDI_SERIAL_2232C)) { return UMATCH_VENDOR_IFACESUBCLASS; } return UMATCH_NONE; } if ((uaa->vendor == USB_VENDOR_FTDI) && ((uaa->product == USB_PRODUCT_FTDI_SERIAL_8U100AX) || (uaa->product == USB_PRODUCT_FTDI_SERIAL_8U232AM) || (uaa->product == USB_PRODUCT_FTDI_SEMC_DSS20) || (uaa->product == USB_PRODUCT_FTDI_CFA_631) || (uaa->product == USB_PRODUCT_FTDI_CFA_632) || (uaa->product == USB_PRODUCT_FTDI_CFA_633) || (uaa->product == USB_PRODUCT_FTDI_CFA_634) || (uaa->product == USB_PRODUCT_FTDI_USBSERIAL) || (uaa->product == USB_PRODUCT_FTDI_MX2_3) || (uaa->product == USB_PRODUCT_FTDI_MX4_5) || (uaa->product == USB_PRODUCT_FTDI_LK202) || (uaa->product == USB_PRODUCT_FTDI_LK204))) { return UMATCH_VENDOR_PRODUCT; } if ((uaa->vendor == USB_VENDOR_SIIG2) && (uaa->product == USB_PRODUCT_SIIG2_US2308)) { return UMATCH_VENDOR_PRODUCT; } if ((uaa->vendor == USB_VENDOR_INTREPIDCS) && ((uaa->product == USB_PRODUCT_INTREPIDCS_VALUECAN) || (uaa->product == USB_PRODUCT_INTREPIDCS_NEOVI))) { return UMATCH_VENDOR_PRODUCT; } if ((uaa->vendor == USB_VENDOR_BBELECTRONICS) && (uaa->product == USB_PRODUCT_BBELECTRONICS_USOTL4)) { return UMATCH_VENDOR_PRODUCT; } return (UMATCH_NONE); } static int uftdi_attach(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct uftdi_softc *sc = device_get_softc(dev); usb_interface_descriptor_t *id; int32_t error; if (sc == NULL) { return ENOMEM; } sc->sc_udev = uaa->device; sc->sc_dev = dev; sc->sc_unit = device_get_unit(dev); usbd_set_desc(dev, uaa->device); snprintf(sc->sc_name, sizeof(sc->sc_name), "%s", device_get_nameunit(dev)); DPRINTF(sc, 0, "\n"); if (uaa->iface == NULL) { error = usbd_set_config_index(uaa->device, UFTDI_CONFIG_INDEX, 1); if (error) { device_printf(dev, "failed to set configuration, " "error=%s\n", usbd_errstr(error)); goto detach; } sc->sc_iface_index = UFTDI_IFACE_INDEX; } else { sc->sc_iface_index = uaa->iface_index; } switch( uaa->vendor ) { case USB_VENDOR_FTDI: switch( uaa->product ){ case USB_PRODUCT_FTDI_SERIAL_8U100AX: sc->sc_type = UFTDI_TYPE_SIO; sc->sc_hdrlen = 1; break; case USB_PRODUCT_FTDI_SEMC_DSS20: case USB_PRODUCT_FTDI_SERIAL_8U232AM: case USB_PRODUCT_FTDI_SERIAL_2232C: case USB_PRODUCT_FTDI_CFA_631: case USB_PRODUCT_FTDI_CFA_632: case USB_PRODUCT_FTDI_CFA_633: case USB_PRODUCT_FTDI_CFA_634: case USB_PRODUCT_FTDI_USBSERIAL: case USB_PRODUCT_FTDI_MX2_3: case USB_PRODUCT_FTDI_MX4_5: case USB_PRODUCT_FTDI_LK202: case USB_PRODUCT_FTDI_LK204: sc->sc_type = UFTDI_TYPE_8U232AM; sc->sc_hdrlen = 0; break; default: /* Can't happen */ goto detach; } break; case USB_VENDOR_INTREPIDCS: switch( uaa->product ){ case USB_PRODUCT_INTREPIDCS_VALUECAN: case USB_PRODUCT_INTREPIDCS_NEOVI: sc->sc_type = UFTDI_TYPE_8U232AM; sc->sc_hdrlen = 0; break; default: /* Can't happen */ goto detach; } break; case USB_VENDOR_SIIG2: switch( uaa->product ){ case USB_PRODUCT_SIIG2_US2308: sc->sc_type = UFTDI_TYPE_8U232AM; sc->sc_hdrlen = 0; break; default: /* Can't happen */ goto detach; } break; case USB_VENDOR_BBELECTRONICS: switch( uaa->product ){ case USB_PRODUCT_BBELECTRONICS_USOTL4: sc->sc_type = UFTDI_TYPE_8U232AM; sc->sc_hdrlen = 0; break; default: /* Can't happen */ goto detach; } break; default: /* Can't happen */ goto detach; } error = usbd_transfer_setup(uaa->device, sc->sc_iface_index, sc->sc_xfer, uftdi_config, UFTDI_ENDPT_MAX, sc, &Giant); if (error) { device_printf(dev, "allocating USB " "transfers failed!\n"); goto detach; } error = usbd_config_td_setup(&(sc->sc_config_td), sc, &Giant, &uftdi_config_copy, NULL, sizeof(struct uftdi_config_copy), 16); if (error) { device_printf(dev, "could not setup config " "thread!\n"); goto detach; } sc->sc_ucom.sc_portno = FTDI_PIT_SIOA; if (uaa->iface) { id = usbd_get_interface_descriptor(uaa->iface); if (id == NULL) { goto detach; } sc->sc_ucom.sc_portno += id->bInterfaceNumber; } error = ucom_attach(&(sc->sc_ucom), 1, sc, &uftdi_callback, &Giant); if (error) { goto detach; } return 0; /* success */ detach: uftdi_detach(dev); return ENXIO; } static int uftdi_detach(device_t dev) { struct uftdi_softc *sc = device_get_softc(dev); mtx_lock(&Giant); usbd_config_td_stop(&(sc->sc_config_td)); mtx_unlock(&Giant); ucom_detach(&(sc->sc_ucom), 1); usbd_transfer_unsetup(sc->sc_xfer, UFTDI_ENDPT_MAX); usbd_config_td_unsetup(&(sc->sc_config_td)); return 0; } static void uftdi_config_copy(struct uftdi_softc *sc, struct uftdi_config_copy *cc, u_int16_t refcount) { bzero(cc, sizeof(*cc)); cc->dtr_onoff = sc->sc_dtr_onoff; cc->rts_onoff = sc->sc_rts_onoff; cc->break_onoff = sc->sc_break_onoff; cc->last_lcr = sc->sc_last_lcr; cc->rate = sc->sc_rate; cc->v_stop = sc->sc_flow_v_stop; cc->v_start = sc->sc_flow_v_start; cc->flow = sc->sc_flow; return; } static void uftdi_cfg_do_request(struct uftdi_softc *sc, usb_device_request_t *req, void *data) { u_int16_t length; usbd_status err; if (usbd_config_td_is_gone(&(sc->sc_config_td))) { goto error; } err = usbd_do_request_flags_mtx(sc->sc_udev, &Giant, req, data, 0, NULL, 1000); if (err) { DPRINTF(sc, 0, "device request failed, err=%s " "(ignored)\n", usbd_errstr(err)); error: length = UGETW(req->wLength); if ((req->bmRequestType & UT_READ) && length) { bzero(data, length); } } return; } static int uftdi_open(struct ucom_softc *ucom) { struct uftdi_softc *sc = ucom->sc_parent; sc->sc_flag |= (UFTDI_FLAG_WRITE_STALL| UFTDI_FLAG_READ_STALL); usbd_config_td_queue_command (&(sc->sc_config_td), &uftdi_cfg_open, 0); return 0; } static void uftdi_cfg_open(struct uftdi_softc *sc, struct uftdi_config_copy *cc, u_int16_t refcount) { u_int16_t wIndex = sc->sc_ucom.sc_portno; usb_device_request_t req; if (cc == NULL) { /* set 9600 baud, 2 stop bits, * no parity, 8 bits */ (void) uftdi_set_parm_soft (sc, 9600, 0, 0, CSTOPB | CS8, 0); return; } DPRINTF(sc, 0, ""); /* perform a full reset on the device */ req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = FTDI_SIO_RESET; USETW(req.wValue, FTDI_SIO_RESET_SIO); USETW(req.wIndex, wIndex); USETW(req.wLength, 0); uftdi_cfg_do_request(sc, &req, NULL); /* write parameters */ uftdi_cfg_parm(sc, cc, 0); /* turn on RTS/CTS flow control */ req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = FTDI_SIO_SET_FLOW_CTRL; USETW(req.wValue, 0); USETW2(req.wIndex, FTDI_SIO_RTS_CTS_HS, wIndex); USETW(req.wLength, 0); uftdi_cfg_do_request(sc, &req, NULL); return; } static void uftdi_write_callback(struct usbd_xfer *xfer) { struct uftdi_softc *sc = xfer->priv_sc; u_int32_t actlen; USBD_CHECK_STATUS(xfer); tr_error: if (xfer->error != USBD_CANCELLED) { sc->sc_flag |= UFTDI_FLAG_WRITE_STALL; usbd_transfer_start(sc->sc_xfer[2]); } return; tr_setup: tr_transferred: if (sc->sc_flag & UFTDI_FLAG_WRITE_STALL) { usbd_transfer_start(sc->sc_xfer[2]); return; } if(ucom_get_data(&(sc->sc_ucom), ((u_int8_t *)(xfer->buffer)) + sc->sc_hdrlen, UFTDI_OBUFSIZE - sc->sc_hdrlen, &actlen)) { if (sc->sc_hdrlen > 0) { *(u_int8_t *)(xfer->buffer) = FTDI_OUT_TAG(actlen, sc->sc_ucom.sc_portno); } xfer->length = actlen + sc->sc_hdrlen; usbd_start_hardware(xfer); } return; } static void uftdi_write_clear_stall_callback(struct usbd_xfer *xfer) { struct uftdi_softc *sc = xfer->priv_sc; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, sc->sc_xfer[0]); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, sc->sc_xfer[0]); sc->sc_flag &= ~UFTDI_FLAG_WRITE_STALL; usbd_transfer_start(sc->sc_xfer[0]); return; tr_error: sc->sc_flag &= ~UFTDI_FLAG_WRITE_STALL; DPRINTF(sc, 0, "clear stall failed, error=%s\n", usbd_errstr(xfer->error)); return; } static void uftdi_read_callback(struct usbd_xfer *xfer) { struct uftdi_softc *sc = xfer->priv_sc; u_int8_t *ptr = xfer->buffer; u_int8_t msr; u_int8_t lsr; USBD_CHECK_STATUS(xfer); tr_error: if (xfer->error != USBD_CANCELLED) { sc->sc_flag |= UFTDI_FLAG_READ_STALL; usbd_transfer_start(sc->sc_xfer[3]); } return; tr_transferred: if (xfer->actlen < 2) { goto tr_setup; } msr = FTDI_GET_MSR(ptr); lsr = FTDI_GET_LSR(ptr); if ((sc->sc_msr != msr) || ((sc->sc_lsr & FTDI_LSR_MASK) != (lsr & FTDI_LSR_MASK))) { DPRINTF(sc, 0, "status change msr=0x%02x (0x%02x) " "lsr=0x%02x (0x%02x)\n", msr, sc->sc_msr, lsr, sc->sc_lsr); sc->sc_msr = msr; sc->sc_lsr = lsr; ucom_status_change(&(sc->sc_ucom)); } xfer->actlen -= 2; ptr += 2; if (xfer->actlen) { ucom_put_data(&(sc->sc_ucom), ptr, xfer->actlen); } tr_setup: if (sc->sc_flag & UFTDI_FLAG_READ_STALL) { usbd_transfer_start(sc->sc_xfer[3]); } else { usbd_start_hardware(xfer); } return; } static void uftdi_read_clear_stall_callback(struct usbd_xfer *xfer) { struct uftdi_softc *sc = xfer->priv_sc; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, sc->sc_xfer[1]); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, sc->sc_xfer[1]); sc->sc_flag &= ~UFTDI_FLAG_READ_STALL; usbd_transfer_start(sc->sc_xfer[1]); return; tr_error: sc->sc_flag &= ~UFTDI_FLAG_READ_STALL; DPRINTF(sc, 0, "clear stall failed, error=%s\n", usbd_errstr(xfer->error)); return; } static void uftdi_set_dtr(struct ucom_softc *ucom, u_int8_t onoff) { struct uftdi_softc *sc = ucom->sc_parent; sc->sc_dtr_onoff = onoff; usbd_config_td_queue_command (&(sc->sc_config_td), &uftdi_cfg_set_dtr, 0); return; } static void uftdi_cfg_set_dtr(struct uftdi_softc *sc, struct uftdi_config_copy *cc, u_int16_t refcount) { u_int16_t wIndex = sc->sc_ucom.sc_portno; u_int16_t wValue; usb_device_request_t req; if (cc == NULL) { /* nothing to do */ return; } wValue = cc->dtr_onoff ? FTDI_SIO_SET_DTR_HIGH : FTDI_SIO_SET_DTR_LOW; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = FTDI_SIO_MODEM_CTRL; USETW(req.wValue, wValue); USETW(req.wIndex, wIndex); USETW(req.wLength, 0); uftdi_cfg_do_request(sc, &req, NULL); return; } static void uftdi_set_rts(struct ucom_softc *ucom, u_int8_t onoff) { struct uftdi_softc *sc = ucom->sc_parent; sc->sc_rts_onoff = onoff; usbd_config_td_queue_command (&(sc->sc_config_td), &uftdi_cfg_set_rts, 0); return; } static void uftdi_cfg_set_rts(struct uftdi_softc *sc, struct uftdi_config_copy *cc, u_int16_t refcount) { u_int16_t wIndex = sc->sc_ucom.sc_portno; u_int16_t wValue; usb_device_request_t req; if (cc == NULL) { /* nothing to do */ return; } wValue = cc->rts_onoff ? FTDI_SIO_SET_RTS_HIGH : FTDI_SIO_SET_RTS_LOW; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = FTDI_SIO_MODEM_CTRL; USETW(req.wValue, wValue); USETW(req.wIndex, wIndex); USETW(req.wLength, 0); uftdi_cfg_do_request(sc, &req, NULL); return; } static void uftdi_set_break(struct ucom_softc *ucom, u_int8_t onoff) { struct uftdi_softc *sc = ucom->sc_parent; sc->sc_break_onoff = onoff; usbd_config_td_queue_command (&(sc->sc_config_td), &uftdi_cfg_set_break, 0); return; } static void uftdi_cfg_set_break(struct uftdi_softc *sc, struct uftdi_config_copy *cc, u_int16_t refcount) { u_int16_t wIndex = sc->sc_ucom.sc_portno; u_int16_t wValue; usb_device_request_t req; if (cc == NULL) { /* nothing to do */ return; } if (cc->break_onoff) { wValue = cc->last_lcr | FTDI_SIO_SET_BREAK; } else { wValue = cc->last_lcr; } req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = FTDI_SIO_SET_DATA; USETW(req.wValue, wValue); USETW(req.wIndex, wIndex); USETW(req.wLength, 0); uftdi_cfg_do_request(sc, &req, NULL); return; } static int uftdi_set_parm_soft(struct uftdi_softc *sc, u_int32_t ospeed, u_int8_t v_start, u_int8_t v_stop, u_int32_t cflag, u_int32_t iflag) { u_int16_t rate = 0; u_int16_t data = 0; u_int8_t flow = 0; switch (sc->sc_type) { case UFTDI_TYPE_SIO: switch (ospeed) { case 300: rate = ftdi_sio_b300; break; case 600: rate = ftdi_sio_b600; break; case 1200: rate = ftdi_sio_b1200; break; case 2400: rate = ftdi_sio_b2400; break; case 4800: rate = ftdi_sio_b4800; break; case 9600: rate = ftdi_sio_b9600; break; case 19200: rate = ftdi_sio_b19200; break; case 38400: rate = ftdi_sio_b38400; break; case 57600: rate = ftdi_sio_b57600; break; case 115200: rate = ftdi_sio_b115200; break; default: return (EINVAL); } break; case UFTDI_TYPE_8U232AM: switch(ospeed) { case 300: rate = ftdi_8u232am_b300; break; case 600: rate = ftdi_8u232am_b600; break; case 1200: rate = ftdi_8u232am_b1200; break; case 2400: rate = ftdi_8u232am_b2400; break; case 4800: rate = ftdi_8u232am_b4800; break; case 9600: rate = ftdi_8u232am_b9600; break; case 19200: rate = ftdi_8u232am_b19200; break; case 38400: rate = ftdi_8u232am_b38400; break; case 57600: rate = ftdi_8u232am_b57600; break; case 115200: rate = ftdi_8u232am_b115200; break; case 230400: rate = ftdi_8u232am_b230400; break; case 460800: rate = ftdi_8u232am_b460800; break; case 921600: rate = ftdi_8u232am_b921600; break; case 2000000: rate = ftdi_8u232am_b2000000; break; case 3000000: rate = ftdi_8u232am_b3000000; break; default: return (EINVAL); } break; } sc->sc_rate = rate; if (cflag & CSTOPB) data = FTDI_SIO_SET_DATA_STOP_BITS_2; else data = FTDI_SIO_SET_DATA_STOP_BITS_1; if (cflag & PARENB) { if (cflag & PARODD) { data |= FTDI_SIO_SET_DATA_PARITY_ODD; } else { data |= FTDI_SIO_SET_DATA_PARITY_EVEN; } } else { data |= FTDI_SIO_SET_DATA_PARITY_NONE; } switch (cflag & CSIZE) { case CS5: data |= FTDI_SIO_SET_DATA_BITS(5); break; case CS6: data |= FTDI_SIO_SET_DATA_BITS(6); break; case CS7: data |= FTDI_SIO_SET_DATA_BITS(7); break; case CS8: data |= FTDI_SIO_SET_DATA_BITS(8); break; } sc->sc_last_lcr = data; if (cflag & CRTSCTS) { flow = FTDI_SIO_RTS_CTS_HS; v_start = 0; v_stop = 0; } else if (iflag & (IXON|IXOFF)) { flow = FTDI_SIO_XON_XOFF_HS; } else { flow = FTDI_SIO_DISABLE_FLOW_CTRL; v_start = 0; v_stop = 0; } sc->sc_flow = flow; sc->sc_flow_v_start = v_start; sc->sc_flow_v_stop = v_stop; return 0; } static int uftdi_param(struct ucom_softc *ucom, struct termios *t) { struct uftdi_softc *sc = ucom->sc_parent; DPRINTF(sc, 0, "\n"); if (uftdi_set_parm_soft(sc, t->c_ospeed, t->c_cc[VSTART], t->c_cc[VSTOP], t->c_cflag, t->c_iflag)) { return EIO; } usbd_config_td_queue_command (&(sc->sc_config_td), &uftdi_cfg_parm, 0); return 0; } static void uftdi_cfg_parm(struct uftdi_softc *sc, struct uftdi_config_copy *cc, u_int16_t refcount) { u_int16_t wIndex = sc->sc_ucom.sc_portno; usb_device_request_t req; if (cc == NULL) { /* nothing to do */ return; } req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = FTDI_SIO_SET_BAUD_RATE; USETW(req.wValue, cc->rate); USETW(req.wIndex, wIndex); USETW(req.wLength, 0); uftdi_cfg_do_request(sc, &req, NULL); req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = FTDI_SIO_SET_DATA; USETW(req.wValue, cc->last_lcr); USETW(req.wIndex, wIndex); USETW(req.wLength, 0); uftdi_cfg_do_request(sc, &req, NULL); req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = FTDI_SIO_SET_FLOW_CTRL; USETW2(req.wValue, cc->v_stop, cc->v_start); USETW2(req.wIndex, cc->flow, wIndex); USETW(req.wLength, 0); uftdi_cfg_do_request(sc, &req, NULL); return; } static void uftdi_get_status(struct ucom_softc *ucom, u_int8_t *lsr, u_int8_t *msr) { struct uftdi_softc *sc = ucom->sc_parent; DPRINTF(sc, 0, "msr=0x%02x lsr=0x%02x\n", sc->sc_msr, sc->sc_lsr); if (msr) { *msr = sc->sc_msr; } if (lsr) { *lsr = sc->sc_lsr; } return; } static void uftdi_start_read(struct ucom_softc *ucom) { struct uftdi_softc *sc = ucom->sc_parent; usbd_transfer_start(sc->sc_xfer[1]); return; } static void uftdi_stop_read(struct ucom_softc *ucom) { struct uftdi_softc *sc = ucom->sc_parent; usbd_transfer_stop(sc->sc_xfer[3]); usbd_transfer_stop(sc->sc_xfer[1]); return; } static void uftdi_start_write(struct ucom_softc *ucom) { struct uftdi_softc *sc = ucom->sc_parent; usbd_transfer_start(sc->sc_xfer[0]); return; } static void uftdi_stop_write(struct ucom_softc *ucom) { struct uftdi_softc *sc = ucom->sc_parent; usbd_transfer_stop(sc->sc_xfer[2]); usbd_transfer_stop(sc->sc_xfer[0]); return; } pwcbsd/usb/uftdireg.h000644 000423 000000 00000022412 10551670754 015364 0ustar00luigiwheel000000 000000 /* $NetBSD: uftdireg.h,v 1.6 2002/07/11 21:14:28 augustss Exp $ */ /* $FreeBSD: src/sys/dev/usb/uftdireg.h,v 1.2 2004/07/01 17:16:20 brooks Exp $ */ /* * Definitions for the FTDI USB Single Port Serial Converter - * known as FTDI_SIO (Serial Input/Output application of the chipset) * * The device is based on the FTDI FT8U100AX chip. It has a DB25 on one side, * USB on the other. * * Thanx to FTDI (http://www.ftdi.co.uk) for so kindly providing details * of the protocol required to talk to the device and ongoing assistence * during development. * * Bill Ryder - bryder@sgi.com of Silicon Graphics, Inc. is the original * author of this file. */ /* Modified by Lennart Augustsson */ /* Vendor Request Interface */ #define FTDI_SIO_RESET 0 /* Reset the port */ #define FTDI_SIO_MODEM_CTRL 1 /* Set the modem control register */ #define FTDI_SIO_SET_FLOW_CTRL 2 /* Set flow control register */ #define FTDI_SIO_SET_BAUD_RATE 3 /* Set baud rate */ #define FTDI_SIO_SET_DATA 4 /* Set the data characteristics of the port */ #define FTDI_SIO_GET_STATUS 5 /* Retrieve current value of status reg */ #define FTDI_SIO_SET_EVENT_CHAR 6 /* Set the event character */ #define FTDI_SIO_SET_ERROR_CHAR 7 /* Set the error character */ /* Port Identifier Table */ #define FTDI_PIT_DEFAULT 0 /* SIOA */ #define FTDI_PIT_SIOA 1 /* SIOA */ #define FTDI_PIT_SIOB 2 /* SIOB */ #define FTDI_PIT_PARALLEL 3 /* Parallel */ enum uftdi_type { UFTDI_TYPE_SIO, UFTDI_TYPE_8U232AM }; /* * BmRequestType: 0100 0000B * bRequest: FTDI_SIO_RESET * wValue: Control Value * 0 = Reset SIO * 1 = Purge RX buffer * 2 = Purge TX buffer * wIndex: Port * wLength: 0 * Data: None * * The Reset SIO command has this effect: * * Sets flow control set to 'none' * Event char = 0x0d * Event trigger = disabled * Purge RX buffer * Purge TX buffer * Clear DTR * Clear RTS * baud and data format not reset * * The Purge RX and TX buffer commands affect nothing except the buffers * */ /* FTDI_SIO_RESET */ #define FTDI_SIO_RESET_SIO 0 #define FTDI_SIO_RESET_PURGE_RX 1 #define FTDI_SIO_RESET_PURGE_TX 2 /* * BmRequestType: 0100 0000B * bRequest: FTDI_SIO_SET_BAUDRATE * wValue: BaudRate value - see below * wIndex: Port * wLength: 0 * Data: None */ /* FTDI_SIO_SET_BAUDRATE */ enum { ftdi_sio_b300 = 0, ftdi_sio_b600 = 1, ftdi_sio_b1200 = 2, ftdi_sio_b2400 = 3, ftdi_sio_b4800 = 4, ftdi_sio_b9600 = 5, ftdi_sio_b19200 = 6, ftdi_sio_b38400 = 7, ftdi_sio_b57600 = 8, ftdi_sio_b115200 = 9 }; enum { ftdi_8u232am_b300 = 0x2710, ftdi_8u232am_b600 = 0x1388, ftdi_8u232am_b1200 = 0x09c4, ftdi_8u232am_b2400 = 0x04e2, ftdi_8u232am_b4800 = 0x0271, ftdi_8u232am_b9600 = 0x4138, ftdi_8u232am_b19200 = 0x809c, ftdi_8u232am_b38400 = 0xc04e, ftdi_8u232am_b57600 = 0x0034, ftdi_8u232am_b115200 = 0x001a, ftdi_8u232am_b230400 = 0x000d, ftdi_8u232am_b460800 = 0x4006, ftdi_8u232am_b921600 = 0x8003, ftdi_8u232am_b2000000 = 0x0001, /* special case for 2M baud */ ftdi_8u232am_b3000000 = 0x0000, /* special case for 3M baud */ }; /* * BmRequestType: 0100 0000B * bRequest: FTDI_SIO_SET_DATA * wValue: Data characteristics (see below) * wIndex: Port * wLength: 0 * Data: No * * Data characteristics * * B0..7 Number of data bits * B8..10 Parity * 0 = None * 1 = Odd * 2 = Even * 3 = Mark * 4 = Space * B11..13 Stop Bits * 0 = 1 * 1 = 1.5 * 2 = 2 * B14..15 Reserved * */ /* FTDI_SIO_SET_DATA */ #define FTDI_SIO_SET_DATA_BITS(n) (n) #define FTDI_SIO_SET_DATA_PARITY_NONE (0x0 << 8) #define FTDI_SIO_SET_DATA_PARITY_ODD (0x1 << 8) #define FTDI_SIO_SET_DATA_PARITY_EVEN (0x2 << 8) #define FTDI_SIO_SET_DATA_PARITY_MARK (0x3 << 8) #define FTDI_SIO_SET_DATA_PARITY_SPACE (0x4 << 8) #define FTDI_SIO_SET_DATA_STOP_BITS_1 (0x0 << 11) #define FTDI_SIO_SET_DATA_STOP_BITS_15 (0x1 << 11) #define FTDI_SIO_SET_DATA_STOP_BITS_2 (0x2 << 11) #define FTDI_SIO_SET_BREAK (0x1 << 14) /* * BmRequestType: 0100 0000B * bRequest: FTDI_SIO_MODEM_CTRL * wValue: ControlValue (see below) * wIndex: Port * wLength: 0 * Data: None * * NOTE: If the device is in RTS/CTS flow control, the RTS set by this * command will be IGNORED without an error being returned * Also - you can not set DTR and RTS with one control message * * ControlValue * B0 DTR state * 0 = reset * 1 = set * B1 RTS state * 0 = reset * 1 = set * B2..7 Reserved * B8 DTR state enable * 0 = ignore * 1 = use DTR state * B9 RTS state enable * 0 = ignore * 1 = use RTS state * B10..15 Reserved */ /* FTDI_SIO_MODEM_CTRL */ #define FTDI_SIO_SET_DTR_MASK 0x1 #define FTDI_SIO_SET_DTR_HIGH (1 | ( FTDI_SIO_SET_DTR_MASK << 8)) #define FTDI_SIO_SET_DTR_LOW (0 | ( FTDI_SIO_SET_DTR_MASK << 8)) #define FTDI_SIO_SET_RTS_MASK 0x2 #define FTDI_SIO_SET_RTS_HIGH (2 | ( FTDI_SIO_SET_RTS_MASK << 8)) #define FTDI_SIO_SET_RTS_LOW (0 | ( FTDI_SIO_SET_RTS_MASK << 8)) /* * BmRequestType: 0100 0000b * bRequest: FTDI_SIO_SET_FLOW_CTRL * wValue: Xoff/Xon * wIndex: Protocol/Port - hIndex is protocl / lIndex is port * wLength: 0 * Data: None * * hIndex protocol is: * B0 Output handshaking using RTS/CTS * 0 = disabled * 1 = enabled * B1 Output handshaking using DTR/DSR * 0 = disabled * 1 = enabled * B2 Xon/Xoff handshaking * 0 = disabled * 1 = enabled * * A value of zero in the hIndex field disables handshaking * * If Xon/Xoff handshaking is specified, the hValue field should contain the * XOFF character and the lValue field contains the XON character. */ /* FTDI_SIO_SET_FLOW_CTRL */ #define FTDI_SIO_DISABLE_FLOW_CTRL 0x0 #define FTDI_SIO_RTS_CTS_HS 0x1 #define FTDI_SIO_DTR_DSR_HS 0x2 #define FTDI_SIO_XON_XOFF_HS 0x4 /* * BmRequestType: 0100 0000b * bRequest: FTDI_SIO_SET_EVENT_CHAR * wValue: Event Char * wIndex: Port * wLength: 0 * Data: None * * wValue: * B0..7 Event Character * B8 Event Character Processing * 0 = disabled * 1 = enabled * B9..15 Reserved * * FTDI_SIO_SET_EVENT_CHAR * * Set the special event character for the specified communications port. * If the device sees this character it will immediately return the * data read so far - rather than wait 40ms or until 62 bytes are read * which is what normally happens. */ /* * BmRequestType: 0100 0000b * bRequest: FTDI_SIO_SET_ERROR_CHAR * wValue: Error Char * wIndex: Port * wLength: 0 * Data: None * * Error Char * B0..7 Error Character * B8 Error Character Processing * 0 = disabled * 1 = enabled * B9..15 Reserved * * * FTDI_SIO_SET_ERROR_CHAR * Set the parity error replacement character for the specified communications * port. */ /* * BmRequestType: 1100 0000b * bRequest: FTDI_SIO_GET_MODEM_STATUS * wValue: zero * wIndex: Port * wLength: 1 * Data: Status * * One byte of data is returned * B0..3 0 * B4 CTS * 0 = inactive * 1 = active * B5 DSR * 0 = inactive * 1 = active * B6 Ring Indicator (RI) * 0 = inactive * 1 = active * B7 Receive Line Signal Detect (RLSD) * 0 = inactive * 1 = active * * FTDI_SIO_GET_MODEM_STATUS * Retrieve the current value of the modem status register. */ #define FTDI_SIO_CTS_MASK 0x10 #define FTDI_SIO_DSR_MASK 0x20 #define FTDI_SIO_RI_MASK 0x40 #define FTDI_SIO_RLSD_MASK 0x80 /* * * DATA FORMAT * * IN Endpoint * * The device reserves the first two bytes of data on this endpoint to contain * the current values of the modem and line status registers. In the absence of * data, the device generates a message consisting of these two status bytes * every 40 ms. * * Byte 0: Modem Status * NOTE: 4 upper bits have same layout as the MSR register in a 16550 * * Offset Description * B0..3 Port * B4 Clear to Send (CTS) * B5 Data Set Ready (DSR) * B6 Ring Indicator (RI) * B7 Receive Line Signal Detect (RLSD) * * Byte 1: Line Status * NOTE: same layout as the LSR register in a 16550 * * Offset Description * B0 Data Ready (DR) * B1 Overrun Error (OE) * B2 Parity Error (PE) * B3 Framing Error (FE) * B4 Break Interrupt (BI) * B5 Transmitter Holding Register (THRE) * B6 Transmitter Empty (TEMT) * B7 Error in RCVR FIFO * * * OUT Endpoint * * This device reserves the first bytes of data on this endpoint contain the * length and port identifier of the message. For the FTDI USB Serial converter * the port identifier is always 1. * * Byte 0: Port & length * * Offset Description * B0..1 Port * B2..7 Length of message - (not including Byte 0) * */ #define FTDI_PORT_MASK 0x0f #define FTDI_MSR_MASK 0xf0 #define FTDI_GET_MSR(p) (((p)[0]) & FTDI_MSR_MASK) #define FTDI_GET_LSR(p) ((p)[1]) #define FTDI_LSR_MASK (~0x60) /* interesting bits */ #define FTDI_OUT_TAG(len, port) (((len) << 2) | (port)) pwcbsd/usb/ugen.c000644 000423 000000 00000123524 10551670754 014512 0ustar00luigiwheel000000 000000 /* $NetBSD: ugen.c,v 1.79 2006/03/01 12:38:13 yamt Exp $ */ /* Also already merged from NetBSD: * $NetBSD: ugen.c,v 1.61 2002/09/23 05:51:20 simonb Exp $ * $NetBSD: ugen.c,v 1.64 2003/06/28 14:21:46 darrenr Exp $ * $NetBSD: ugen.c,v 1.65 2003/06/29 22:30:56 fvdl Exp $ * $NetBSD: ugen.c,v 1.68 2004/06/23 02:30:52 mycroft Exp $ */ #include __FBSDID("$FreeBSD: src/sys/dev/usb/ugen.c,v 1.108 2006/09/06 23:29:53 imp Exp $"); /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define UGEN_BULK_BUFFER_SIZE (1024*64) /* bytes */ #define UGEN_HW_FRAMES 50 /* number of milliseconds per transfer */ #define ADD_BYTES(ptr,size) ((void *)(((u_int8_t *)(ptr)) + (size))) struct ugen_frame_ring { u_int16_t input_index; u_int16_t output_index; u_int16_t end_index; /* exclusive */ u_int16_t frame_size; u_int16_t *frlengths; void *buf; }; struct ugen_endpoint { struct ugen_softc * sc; struct cdev * dev; struct usbd_pipe * pipe_in; /* pipe for reading data from USB */ struct usbd_pipe * pipe_out; /* pipe for writing data to USB */ struct usbd_xfer * xfer_in[2]; struct usbd_xfer * xfer_out[2]; struct ugen_frame_ring in_queue; /* (isoc/interrupt) */ struct ugen_frame_ring out_queue; /* (isoc) */ u_int32_t in_timeout; /* (bulk/interrupt) */ u_int32_t in_frames; /* number of frames to use (isoc) */ u_int32_t out_frames; /* number of frames to use (isoc) */ u_int16_t out_frame_size; /* maximum frame size (isoc) */ u_int32_t io_buffer_size; /* (bulk) */ struct selinfo selinfo; u_int16_t state; #define UGEN_OPEN_IN 0x0001 #define UGEN_OPEN_OUT 0x0002 #define UGEN_OPEN_DEV 0x0004 #define UGEN_CLOSING 0x0008 #define UGEN_GONE 0x0010 #define UGEN_SHORT_OK 0x0020 /* short xfers are OK */ /* context bits */ #define UGEN_RD_CFG 0x0040 #define UGEN_RD_SLP 0x0080 /* sleep entered */ #define UGEN_RD_WUP 0x0100 /* need wakeup */ #define UGEN_RD_UIO 0x0200 /* context bits */ #define UGEN_WR_CFG 0x0400 #define UGEN_WR_SLP 0x0800 #define UGEN_WR_WUP 0x1000 #define UGEN_WR_UIO 0x2000 /* context bit */ #define UGEN_IOCTL 0x4000 }; struct ugen_softc { device_t sc_dev; struct usbd_device * sc_udev; struct ugen_endpoint sc_endpoints[USB_MAX_ENDPOINTS]; struct ugen_endpoint sc_endpoints_end[0]; struct mtx sc_mtx; }; extern cdevsw_t ugen_cdevsw; static void ugen_make_devnodes(struct ugen_softc *sc); static void ugen_destroy_devnodes(struct ugen_softc *sc, int skip_first); static void ugen_interrupt_callback(struct usbd_xfer *xfer); static void ugenisoc_read_callback(struct usbd_xfer *xfer); static void ugenisoc_write_callback(struct usbd_xfer *xfer); static int ugen_set_config(struct ugen_softc *sc, int configno); static int ugen_set_interface(struct ugen_softc *sc, int ifaceidx, int altno); static usb_config_descriptor_t * ugen_get_cdesc(struct usbd_device *udev, int index, int *lenp); static int ugen_get_alt_index(struct usbd_device *udev, int ifaceidx); #define UGENMINOR(unit, endpoint) (((unit) << 4) | (endpoint)) #define DEV2SC(dev) ((dev)->si_drv1) #define DEV2SCE(dev) ((dev)->si_drv2) static int ugen_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); if(uaa->usegeneric) return (UMATCH_GENERIC); else return (UMATCH_NONE); } static int ugen_attach(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct ugen_softc *sc = device_get_softc(dev); struct ugen_endpoint *sce; int conf; sc->sc_dev = dev; sc->sc_udev = uaa->device; mtx_init(&sc->sc_mtx, "ugen lock", NULL, MTX_DEF|MTX_RECURSE); /**/ usbd_set_desc(dev, sc->sc_udev); /* first set configuration index 0, the default one for ugen */ if(usbd_set_config_index(sc->sc_udev, 0, 0)) { device_printf(dev, "setting configuration index 0 failed\n"); return ENXIO; } conf = usbd_get_config_descriptor(sc->sc_udev)->bConfigurationValue; /* set up all the local state for this configuration */ if(ugen_set_config(sc, conf)) { device_printf(dev, "setting configuration " "%d failed\n", conf); return ENXIO; } sce = &sc->sc_endpoints[0]; /* make control endpoint */ sce->dev = make_dev(&ugen_cdevsw, UGENMINOR(device_get_unit(sc->sc_dev), 0), UID_ROOT, GID_OPERATOR, 0644, "%s", device_get_nameunit(sc->sc_dev)); if(sce->dev) { DEV2SC(sce->dev) = sc; DEV2SCE(sce->dev) = sce; } return 0; /* success */ } static int ugen_detach(device_t dev) { struct ugen_softc *sc = device_get_softc(dev); struct ugen_endpoint *sce = &sc->sc_endpoints[0]; struct ugen_endpoint *sce_end = &sc->sc_endpoints_end[0]; mtx_lock(&sc->sc_mtx); while(sce < sce_end) { sce->state |= UGEN_GONE; sce++; } mtx_unlock(&sc->sc_mtx); /* destroy all devices */ ugen_destroy_devnodes(sc, 0); mtx_destroy(&sc->sc_mtx); return 0; } #define ugen_inc_input_index(ufr) \ {\ (ufr)->input_index++;\ if((ufr)->input_index >= (ufr)->end_index)\ (ufr)->input_index = 0;\ } #define ugen_inc_output_index(ufr) \ {\ (ufr)->output_index++;\ if((ufr)->output_index >= (ufr)->end_index)\ (ufr)->output_index = 0;\ } #define ugen_half_empty(ufr) \ ((ufr)->end_index ? \ ((((ufr)->end_index + \ (ufr)->input_index - \ (ufr)->output_index) % (ufr)->end_index) \ < ((ufr)->end_index / 2)) : 0) #define ugen_not_empty(ufr) \ ((ufr)->output_index != \ (ufr)->input_index) static void ugen_get_input_block(struct ugen_frame_ring *ufr, void **ptr, u_int16_t **len) { u_int16_t next; next = ufr->input_index +1; if(next >= ufr->end_index) { next = 0; } if(next == ufr->output_index) { /* buffer full */ *ptr = 0; *len = 0; } else { *ptr = ADD_BYTES (ufr->buf, ufr->frame_size*ufr->input_index); *len = &ufr->frlengths[ufr->input_index]; /* set default block length */ ufr->frlengths[ufr->input_index] = ufr->frame_size; } return; } static void ugen_get_output_block(struct ugen_frame_ring *ufr, void **ptr, u_int16_t *len) { if(ufr->output_index == ufr->input_index) { /* buffer empty */ *ptr = 0; *len = 0; } else { *ptr = ADD_BYTES (ufr->buf, ufr->frame_size*ufr->output_index); *len = ufr->frlengths[ufr->output_index]; } return; } static int ugen_allocate_blocks(struct ugen_softc *sc, struct ugen_endpoint *sce, u_int16_t context_bit, struct ugen_frame_ring *ufr, u_int16_t frames, u_int16_t frame_size) { void *ptr; bzero(ufr, sizeof(*ufr)); if(frames == 0) { return 0; } /* one frame will always be unused * to make things simple, so allocate * one extra frame: */ frames++; sce->state |= context_bit; mtx_unlock(&sc->sc_mtx); ptr = malloc(frames*(frame_size + sizeof(u_int16_t)), M_USBDEV, M_WAITOK); mtx_lock(&sc->sc_mtx); sce->state &= ~context_bit; if(sce->state & UGEN_CLOSING) { wakeup(sce); if(ptr) { free(ptr, M_USBDEV); ptr = NULL; } } if(ptr == NULL) { return 0; } ufr->end_index = frames; ufr->frame_size = frame_size; ufr->frlengths = ADD_BYTES(ptr, frames*frame_size); ufr->buf = ADD_BYTES(ptr, 0); return 1; } static void ugen_free_blocks(struct ugen_frame_ring *ufr) { if(ufr->buf) { free(ufr->buf, M_USBDEV); ufr->buf = NULL; } return; } static usbd_status __usbd_transfer_setup(struct ugen_softc *sc, struct ugen_endpoint *sce, u_int16_t context_bit, struct usbd_device *udev, u_int8_t iface_index, struct usbd_xfer **pxfer, const struct usbd_config *setup, u_int8_t n_setup) { struct usbd_xfer * temp[n_setup]; usbd_status error; sce->state |= context_bit; mtx_unlock(&sc->sc_mtx); /* "usbd_transfer_setup()" can sleep so one * needs to make a wrapper, exiting * the mutex and checking things */ error = usbd_transfer_setup(udev, iface_index, &temp[0], setup, n_setup, sce, &(sc->sc_mtx)); mtx_lock(&sc->sc_mtx); if(sce->state & UGEN_CLOSING) { mtx_unlock(&(sc->sc_mtx)); /* "usbd_transfer_unsetup()" will clear "temp[]" */ usbd_transfer_unsetup(&temp[0], n_setup); mtx_lock(&(sc->sc_mtx)); wakeup(sce); error = USBD_CANCELLED; } sce->state &= ~context_bit; while(n_setup--) { pxfer[n_setup] = temp[n_setup]; } return error; } static int __uiomove(struct ugen_softc *sc, struct ugen_endpoint *sce, u_int16_t context_bit, void *cp, int n, struct uio *uio) { int error; sce->state |= context_bit; mtx_unlock(&sc->sc_mtx); /* "uiomove()" can sleep so one * needs to make a wrapper, exiting * the mutex and checking things */ error = uiomove(cp, n, uio); mtx_lock(&sc->sc_mtx); sce->state &= ~context_bit; if(sce->state & UGEN_CLOSING) { wakeup(sce); error = EINTR; } return error; } static int ugenopen(struct cdev *dev, int flag, int mode, struct thread *p) { struct ugen_softc *sc = DEV2SC(dev); struct ugen_endpoint *sce = DEV2SCE(dev); int error = 0; PRINTFN(5, ("flag=%d, mode=%d\n", flag, mode)); if((sc == NULL) || (sce == NULL)) { return (EIO); } mtx_lock(&sc->sc_mtx); if(sce->state & (UGEN_OPEN_DEV|UGEN_GONE)) { error = EBUSY; goto done; } if(sce != &sc->sc_endpoints[0]) { /* non-control endpoint(s) ; * make sure that there are * pipes for all directions */ if(((flag & FWRITE) && !sce->pipe_out) || ((flag & FREAD) && !sce->pipe_in)) { error = ENXIO; goto done; } } sce->state |= UGEN_OPEN_DEV; done: mtx_unlock(&sc->sc_mtx); return error; } static int ugenclose(struct cdev *dev, int flag, int mode, struct thread *p) { struct ugen_softc *sc = DEV2SC(dev); struct ugen_endpoint *sce = DEV2SCE(dev); struct usbd_xfer *temp_xfer[4]; PRINTFN(5, ("flag=%d, mode=%d\n", flag, mode)); if((sc == NULL) || (sce == NULL)) { return (0); } mtx_lock(&sc->sc_mtx); if(sce->state & (UGEN_OPEN_DEV|UGEN_OPEN_IN|UGEN_OPEN_OUT)) { /* control endpoint is also ``closed'' here */ sce->state |= UGEN_CLOSING; if(sce->xfer_in[0]) { usbd_transfer_stop(sce->xfer_in[0]); } if(sce->xfer_in[1]) { usbd_transfer_stop(sce->xfer_in[1]); } if(sce->xfer_out[0]) { usbd_transfer_stop(sce->xfer_out[0]); } if(sce->xfer_out[1]) { usbd_transfer_stop(sce->xfer_out[1]); } while(sce->state & (UGEN_RD_CFG|UGEN_RD_SLP|UGEN_RD_WUP|UGEN_RD_UIO| UGEN_WR_CFG|UGEN_WR_SLP|UGEN_WR_WUP|UGEN_WR_UIO| UGEN_IOCTL)) { if(sce->state & (UGEN_RD_WUP|UGEN_WR_WUP)) { sce->state &= ~(UGEN_RD_WUP|UGEN_WR_WUP); wakeup(sce); } /* wait for routine(s) to exit */ msleep(sce, &sc->sc_mtx, PRIBIO, "ugensync", 0); } /* free all memory after that one has * waited for all context bits to clear, * hence functions accessing memory * like "uiomove", might be sleeping ! */ ugen_free_blocks(&sce->in_queue); ugen_free_blocks(&sce->out_queue); sce->state &= ~(UGEN_OPEN_DEV|UGEN_OPEN_IN|UGEN_OPEN_OUT|UGEN_CLOSING); temp_xfer[0] = sce->xfer_in[0]; temp_xfer[1] = sce->xfer_in[1]; temp_xfer[2] = sce->xfer_out[0]; temp_xfer[3] = sce->xfer_out[1]; sce->xfer_in[0] = NULL; sce->xfer_in[1] = NULL; sce->xfer_out[0] = NULL; sce->xfer_out[1] = NULL; mtx_unlock(&sc->sc_mtx); usbd_transfer_unsetup(temp_xfer, 4); } else { mtx_unlock(&sc->sc_mtx); } return (0); } static int ugen_open_pipe_write(struct ugen_softc *sc, struct ugen_endpoint *sce) { u_int16_t isize; usbd_status err; mtx_assert(&sc->sc_mtx, MA_OWNED); if(!(sce->state & UGEN_OPEN_OUT)) { if(sce->pipe_out) { usb_endpoint_descriptor_t *ed = sce->pipe_out->edesc; struct usbd_config usbd_config[2] = { /* zero */ }; usbd_config[1].type = UE_CONTROL; usbd_config[1].endpoint = 0; usbd_config[1].direction = -1; usbd_config[1].timeout = USBD_DEFAULT_TIMEOUT; usbd_config[1].flags = 0; usbd_config[1].bufsize = sizeof(usb_device_request_t); usbd_config[1].callback = &usbd_clearstall_callback; usbd_config[0].type = ed->bmAttributes & UE_XFERTYPE; usbd_config[0].endpoint = ed->bEndpointAddress & UE_ADDR; usbd_config[0].direction = UE_DIR_OUT; usbd_config[0].callback = usbd_default_callback; usbd_config[0].interval = USBD_DEFAULT_INTERVAL; usbd_config[0].timeout = sce->in_timeout; switch(ed->bmAttributes & UE_XFERTYPE) { case UE_INTERRUPT: case UE_BULK: usbd_config[0].flags = USBD_SYNCHRONOUS; usbd_config[0].bufsize = UGEN_BULK_BUFFER_SIZE; if(__usbd_transfer_setup (sc, sce, UGEN_WR_CFG, sc->sc_udev, sce->pipe_out->iface_index, &sce->xfer_out[0], &usbd_config[0], 2)) { return (EIO); } /* setup clear stall */ sce->xfer_out[0]->clearstall_xfer = sce->xfer_out[1]; break; case UE_ISOCHRONOUS: isize = UGETW(ed->wMaxPacketSize); /* wMaxPacketSize is validated * by "usbd_fill_iface_data()" */ if(usbd_get_speed(sc->sc_udev) == USB_SPEED_HIGH) { sce->out_frames = UGEN_HW_FRAMES*8; } else { sce->out_frames = UGEN_HW_FRAMES; } if(ugen_allocate_blocks (sc, sce, UGEN_WR_CFG, &sce->out_queue, sce->out_frames * 10, isize) == 0) { return ENOMEM; } usbd_config[0].flags = USBD_SHORT_XFER_OK; usbd_config[0].bufsize = isize * sce->out_frames; usbd_config[0].frames = sce->out_frames; usbd_config[0].callback = ugenisoc_write_callback; usbd_config[0].timeout = 0; err = __usbd_transfer_setup (sc, sce, UGEN_WR_CFG, sc->sc_udev, sce->pipe_out->iface_index, &sce->xfer_out[0], &usbd_config[0], 1); if(!err) { err = __usbd_transfer_setup (sc, sce, UGEN_WR_CFG, sc->sc_udev, sce->pipe_out->iface_index, &sce->xfer_out[1], &usbd_config[0], 1); } if(err) { usbd_transfer_unsetup(&sce->xfer_out[0], 1); usbd_transfer_unsetup(&sce->xfer_out[1], 1); ugen_free_blocks(&sce->out_queue); return (EIO); } break; default: return (EINVAL); } sce->state |= UGEN_OPEN_OUT; } else { return ENXIO; } } return 0; } static int ugen_open_pipe_read(struct ugen_softc *sc, struct ugen_endpoint *sce) { int isize; usbd_status err; mtx_assert(&sc->sc_mtx, MA_OWNED); if(!(sce->state & UGEN_OPEN_IN)) { if(sce->pipe_in) { usb_endpoint_descriptor_t *ed = sce->pipe_in->edesc; struct usbd_config usbd_config[2] = { /* zero */ }; usbd_config[1].type = UE_CONTROL; usbd_config[1].endpoint = 0; usbd_config[1].direction = -1; usbd_config[1].timeout = USBD_DEFAULT_TIMEOUT; usbd_config[1].flags = 0; usbd_config[1].bufsize = sizeof(usb_device_request_t); usbd_config[1].callback = &usbd_clearstall_callback; usbd_config[0].type = ed->bmAttributes & UE_XFERTYPE; usbd_config[0].endpoint = ed->bEndpointAddress & UE_ADDR; usbd_config[0].direction = UE_DIR_IN; usbd_config[0].timeout = sce->in_timeout; switch(ed->bmAttributes & UE_XFERTYPE) { case UE_INTERRUPT: usbd_config[0].flags = USBD_SHORT_XFER_OK; usbd_config[0].callback = ugen_interrupt_callback; usbd_config[0].bufsize = UGETW(ed->wMaxPacketSize); usbd_config[0].interval = USBD_DEFAULT_INTERVAL; if(ugen_allocate_blocks (sc, sce, UGEN_RD_CFG, &sce->in_queue, 1, UGETW(ed->wMaxPacketSize)) == 0) { return ENOMEM; } if(__usbd_transfer_setup (sc, sce, UGEN_RD_CFG, sc->sc_udev, sce->pipe_in->iface_index, &sce->xfer_in[0], &usbd_config[0], 2)) { ugen_free_blocks(&sce->in_queue); return (EIO); } /* setup clear stall */ sce->xfer_in[0]->clearstall_xfer = sce->xfer_in[1]; usbd_transfer_start(sce->xfer_in[0]); PRINTFN(5, ("interrupt open done\n")); break; case UE_BULK: usbd_config[0].flags = ((sce->state & UGEN_SHORT_OK) ? USBD_SHORT_XFER_OK : 0) | USBD_SYNCHRONOUS; usbd_config[0].callback = usbd_default_callback; usbd_config[0].bufsize = UGEN_BULK_BUFFER_SIZE; if(__usbd_transfer_setup (sc, sce, UGEN_RD_CFG, sc->sc_udev, sce->pipe_in->iface_index, &sce->xfer_in[0], &usbd_config[0], 2)) { return (EIO); } /* setup clear stall */ sce->xfer_in[0]->clearstall_xfer = sce->xfer_in[1]; break; case UE_ISOCHRONOUS: isize = UGETW(ed->wMaxPacketSize); /* wMaxPacketSize is validated by "usbd_fill_iface_data()" */ if(usbd_get_speed(sc->sc_udev) == USB_SPEED_HIGH) { sce->in_frames = UGEN_HW_FRAMES*8; } else { sce->in_frames = UGEN_HW_FRAMES; } if(ugen_allocate_blocks (sc, sce, UGEN_RD_CFG, &sce->in_queue, sce->in_frames * 10, isize) == 0) { return ENOMEM; } usbd_config[0].flags = USBD_SHORT_XFER_OK; usbd_config[0].bufsize = isize * sce->in_frames; usbd_config[0].frames = sce->in_frames; usbd_config[0].callback = ugenisoc_read_callback; usbd_config[0].timeout = 0; err = __usbd_transfer_setup (sc, sce, UGEN_RD_CFG, sc->sc_udev, sce->pipe_in->iface_index, &sce->xfer_in[0], &usbd_config[0], 1); if(!err) { err = __usbd_transfer_setup (sc, sce, UGEN_RD_CFG, sc->sc_udev, sce->pipe_in->iface_index, &sce->xfer_in[1], &usbd_config[0], 1); } if(err) { usbd_transfer_unsetup(&sce->xfer_in[0], 1); usbd_transfer_unsetup(&sce->xfer_in[1], 1); ugen_free_blocks(&sce->in_queue); return (EIO); } usbd_transfer_start(sce->xfer_in[0]); usbd_transfer_start(sce->xfer_in[1]); PRINTFN(5, ("isoc open done\n")); break; default: return EINVAL; } sce->state |= UGEN_OPEN_IN; } else { return ENXIO; } } return 0; } static void ugen_make_devnodes(struct ugen_softc *sc) { struct usbd_pipe *pipe; struct usbd_pipe *pipe_end; struct ugen_endpoint *sce; struct cdev *dev; int endpoint; mtx_lock(&sc->sc_mtx); pipe = &sc->sc_udev->pipes[0]; pipe_end = &sc->sc_udev->pipes_end[0]; while(pipe < pipe_end) { if(pipe->edesc) { endpoint = pipe->edesc->bEndpointAddress & UE_ADDR; sce = &sc->sc_endpoints[endpoint]; if(!sce->dev && (endpoint != 0)) { mtx_unlock(&sc->sc_mtx); /* XXX "make_dev()" can sleep, * XXX caller should have * XXX set a context bit ! */ dev = make_dev(&ugen_cdevsw, UGENMINOR(device_get_unit(sc->sc_dev), endpoint), UID_ROOT, GID_OPERATOR, 0644, "%s.%d", device_get_nameunit(sc->sc_dev), endpoint); mtx_lock(&sc->sc_mtx); /* XXX */ sce->dev = dev; if(sce->dev) { DEV2SCE(sce->dev) = sce; DEV2SC(sce->dev) = sc; } } sce->in_timeout = USBD_NO_TIMEOUT; sce->out_frame_size = -1; /* set maximum value */ sce->io_buffer_size = UGEN_BULK_BUFFER_SIZE; /* set default value */ if((pipe->edesc->bEndpointAddress & (UE_DIR_IN|UE_DIR_OUT)) == UE_DIR_IN) { sce->pipe_in = pipe; } else { sce->pipe_out = pipe; } } pipe++; } mtx_unlock(&sc->sc_mtx); return; } static void ugen_destroy_devnodes(struct ugen_softc *sc, int skip_first) { struct ugen_endpoint *sce = &sc->sc_endpoints[0]; struct ugen_endpoint *sce_end = &sc->sc_endpoints_end[0]; if(skip_first) { sce++; /* skip control endpoint */ } while(sce < sce_end) { if(sce->dev) { ugenclose(sce->dev, 0, 0, 0); DEV2SCE(sce->dev) = NULL; DEV2SC(sce->dev) = NULL; destroy_dev(sce->dev); } sce->pipe_in = NULL; sce->pipe_out = NULL; sce->dev = NULL; sce++; } return; } static int ugenread(struct cdev *dev, struct uio *uio, int flag) { struct ugen_softc *sc = DEV2SC(dev); struct ugen_endpoint *sce = DEV2SCE(dev); struct usbd_xfer *xfer; void *ptr; int error; int n; u_int16_t len; PRINTFN(5, ("\n")); if((sc == NULL) || (sce == NULL)) { return (EIO); } /* check for control endpoint */ if(sce == &sc->sc_endpoints[0]) { return (ENODEV); } mtx_lock(&sc->sc_mtx); if(sce->state & (UGEN_CLOSING|UGEN_GONE|UGEN_RD_CFG| UGEN_RD_UIO|UGEN_RD_SLP)) { error = EIO; goto done; } error = ugen_open_pipe_read(sc,sce); if(error) { goto done; } switch (sce->pipe_in->edesc->bmAttributes & UE_XFERTYPE) { case UE_ISOCHRONOUS: n = 1; goto ue_interrupt; case UE_INTERRUPT: n = 2; ue_interrupt: while(uio->uio_resid) { if((uio->uio_resid < sce->in_queue.frame_size) && (n == 0)) { /* try to keep data synchronization */ break; } /* get one frame from input queue */ ugen_get_output_block(&sce->in_queue, &ptr, &len); if(ptr == NULL) { if(n == 0) { /* let application process data */ break; } if(flag & IO_NDELAY) { if(n) { error = EWOULDBLOCK; } break; } /* wait for data */ sce->state |= (UGEN_RD_SLP|UGEN_RD_WUP); error = msleep(sce, &sc->sc_mtx, (PZERO|PCATCH), "ugen wait callback", 0); sce->state &= ~(UGEN_RD_SLP|UGEN_RD_WUP); if(sce->state & UGEN_CLOSING) { wakeup(sce); error = EIO; break; } if(error) { break; } continue; } if(len > uio->uio_resid) { PRINTFN(5, ("dumping %d bytes!\n", len - (u_int16_t)(uio->uio_resid))); /* rest of this frame will get dumped * for sake of synchronization! */ len = uio->uio_resid; } PRINTFN(10, ("transferring %d bytes\n", len)); /* copy data to user memory */ error = __uiomove(sc, sce, UGEN_RD_UIO, ptr, len, uio); if(error) break; ugen_inc_output_index(&sce->in_queue); /* only transfer one interrupt frame per read ! */ if(n == 2) { /* start interrupt transfer again */ usbd_transfer_start(sce->xfer_in[0]); break; } n = 0; } break; case UE_BULK: while((n = min(UGEN_BULK_BUFFER_SIZE, uio->uio_resid)) != 0) { #if 0 if(flag & IO_NDELAY) { error = EWOULDBLOCK; break; } #endif xfer = sce->xfer_in[0]; /* update length */ xfer->length = n; sce->state |= UGEN_RD_UIO; /* start transfer */ usbd_transfer_start(xfer); sce->state &= ~UGEN_RD_UIO; if(sce->state & UGEN_CLOSING) { error = EIO; wakeup(sce); break; } if(xfer->error) { error = (xfer->error == USBD_CANCELLED) ? EINTR : EIO; break; } PRINTFN(1, ("got %d of %d bytes\n", xfer->actlen, n)); error = __uiomove (sc, sce, UGEN_RD_UIO, xfer->buffer, xfer->actlen, uio); if(error || (xfer->actlen < n)) { break; } } break; default: error = ENXIO; break; } done: mtx_unlock(&sc->sc_mtx); return (error); } static int ugenwrite(struct cdev *dev, struct uio *uio, int flag) { struct ugen_softc *sc = DEV2SC(dev); struct ugen_endpoint *sce = DEV2SCE(dev); struct usbd_xfer *xfer; u_int16_t *plen; void *ptr; int error; int n; PRINTFN(5, ("\n")); if((sc == NULL) || (sce == NULL)) { return (EIO); } /* check for control endpoint */ if(sce == &sc->sc_endpoints[0]) { return (ENODEV); } mtx_lock(&sc->sc_mtx); if(sce->state & (UGEN_CLOSING|UGEN_GONE|UGEN_WR_CFG| UGEN_WR_SLP|UGEN_WR_UIO)) { error = EIO; goto done; } error = ugen_open_pipe_write(sc,sce); if(error) { goto done; } switch (sce->pipe_out->edesc->bmAttributes & UE_XFERTYPE) { case UE_BULK: case UE_INTERRUPT: while((n = min(UGEN_BULK_BUFFER_SIZE, uio->uio_resid)) != 0) { #if 0 if(flag & IO_NDELAY) { error = EWOULDBLOCK; break; } #endif xfer = sce->xfer_out[0]; error = __uiomove (sc, sce, UGEN_WR_UIO, xfer->buffer, n, uio); if(error) { break; } PRINTFN(1, ("transferred %d bytes\n", n)); /* update length */ xfer->length = n; sce->state |= UGEN_WR_UIO; /* start transfer */ usbd_transfer_start(xfer); sce->state &= ~UGEN_WR_UIO; if(sce->state & UGEN_CLOSING) { error = EIO; wakeup(sce); break; } if(xfer->error) { error = (xfer->error == USBD_CANCELLED) ? EINTR : EIO; break; } } break; case UE_ISOCHRONOUS: n = 1; while(uio->uio_resid || n) { ugen_get_input_block(&sce->out_queue, &ptr, &plen); if(ptr == NULL) { /* make sure that the transfers are * started, if not already started. */ usbd_transfer_start(sce->xfer_out[0]); usbd_transfer_start(sce->xfer_out[1]); if(flag & IO_NDELAY) { if(n) { error = EWOULDBLOCK; } break; } sce->state |= (UGEN_WR_SLP|UGEN_WR_WUP); error = msleep(sce, &sc->sc_mtx, (PZERO|PCATCH), "ugen wait callback", 0); sce->state &= ~(UGEN_WR_SLP|UGEN_WR_WUP); if(sce->state & UGEN_CLOSING) { wakeup(sce); error = EIO; break; } if(error) { break; } continue; } if(*plen > sce->out_frame_size) { *plen = sce->out_frame_size; } if(*plen > uio->uio_resid) { *plen = uio->uio_resid; } error = __uiomove(sc, sce, UGEN_WR_UIO, ptr, *plen, uio); if(error) break; ugen_inc_input_index(&sce->out_queue); n = 0; } if(n == 0) { /* make sure that the transfers are * started, if not already started. */ usbd_transfer_start(sce->xfer_out[0]); usbd_transfer_start(sce->xfer_out[1]); } break; default: error = ENXIO; break; } done: mtx_unlock(&sc->sc_mtx); return (error); } static void ugen_interrupt_callback(struct usbd_xfer *xfer) { struct ugen_endpoint *sce = xfer->priv_sc; u_int16_t *plen; void *ptr; USBD_CHECK_STATUS(xfer); tr_transferred: PRINTFN(5, ("xfer=%p actlen=%d\n", xfer, xfer->actlen)); ugen_get_input_block(&sce->in_queue, &ptr, &plen); if(ptr == NULL) { PRINTFN(5, ("dropping one packet, sce=%p\n", sce)); } else { bcopy(xfer->buffer, ptr, xfer->actlen); if(xfer->actlen > *plen) { xfer->actlen = *plen; } *plen = xfer->actlen; ugen_inc_input_index(&sce->in_queue); } if(sce->state & UGEN_RD_WUP) { sce->state &= ~UGEN_RD_WUP; PRINTFN(5, ("waking %p\n", sce)); wakeup(sce); } selwakeuppri(&sce->selinfo, PZERO); /* the transfer will be restarted after that * the packet has been read */ return; tr_setup: tr_error: usbd_start_hardware(xfer); return; } static void ugenisoc_read_callback(struct usbd_xfer *xfer) { struct ugen_endpoint *sce = xfer->priv_sc; u_int16_t *plen1; u_int16_t *plen2; void *ptr1; void *ptr2; u_int16_t isize; u_int16_t n; USBD_CHECK_STATUS(xfer); tr_transferred: PRINTFN(5,("actlen=%d\n", xfer->actlen)); plen1 = xfer->frlengths; ptr1 = xfer->buffer; isize = UGETW(sce->pipe_in->edesc->wMaxPacketSize); n = sce->in_frames; while(n--) { if(*plen1 != 0) { ugen_get_input_block(&sce->in_queue, &ptr2, &plen2); if(ptr2 == NULL) { break; } if(*plen1 > *plen2) { *plen1 = *plen2; } bcopy(ptr1, ptr2, *plen1); *plen2 = *plen1; ugen_inc_input_index(&sce->in_queue); } ptr1 = ADD_BYTES(ptr1, isize); plen1++; } if(sce->state & UGEN_RD_WUP) { sce->state &= ~UGEN_RD_WUP; wakeup(sce); } selwakeuppri(&sce->selinfo, PZERO); tr_setup: tr_error: isize = UGETW(sce->pipe_in->edesc->wMaxPacketSize); for(n = 0; n < sce->in_frames; n++) { /* setup size for next transfer */ xfer->frlengths[n] = isize; } usbd_start_hardware(xfer); return; } static void ugenisoc_write_callback(struct usbd_xfer *xfer) { struct ugen_endpoint *sce = xfer->priv_sc; u_int16_t *plen1; u_int16_t len2; void *ptr1; void *ptr2; u_int16_t isize; u_int16_t n; USBD_CHECK_STATUS(xfer); tr_transferred: tr_setup: plen1 = xfer->frlengths; ptr1 = xfer->buffer; isize = UGETW(sce->pipe_out->edesc->wMaxPacketSize); n = sce->out_frames; while(n--) { ugen_get_output_block(&sce->out_queue, &ptr2, &len2); if(ptr2 == NULL) { break; } if(len2 > isize) { len2 = isize; } bcopy(ptr2, ptr1, len2); *plen1 = len2; ugen_inc_output_index(&sce->out_queue); ptr1 = ADD_BYTES(ptr1, len2); plen1++; } n = plen1 - xfer->frlengths; /* update number of frames */ xfer->nframes = n; if(sce->state & UGEN_WR_WUP) { sce->state &= ~UGEN_WR_WUP; wakeup(sce); } selwakeuppri(&sce->selinfo, PZERO); if(n) { tr_error: usbd_start_hardware(xfer); } return; } static int ugen_set_config(struct ugen_softc *sc, int configno) { PRINTFN(1,("configno %d, sc=%p\n", configno, sc)); /* destroy all but control device */ ugen_destroy_devnodes(sc, 1); /* avoid setting the current value */ if((usbd_get_config_descriptor(sc->sc_udev) == NULL) || (usbd_get_config_descriptor(sc->sc_udev)->bConfigurationValue != configno)) { if(usbd_set_config_no(sc->sc_udev, configno, 1)) { return EIO; } } /* make devices */ ugen_make_devnodes(sc); return 0; } static int ugen_set_interface(struct ugen_softc *sc, int ifaceidx, int altno) { PRINTFN(15, ("%d %d\n", ifaceidx, altno)); /* destroy all but control device */ ugen_destroy_devnodes(sc, 1); /* change setting */ if(usbreq_set_interface(sc->sc_udev, ifaceidx, altno)) { return EIO; } /* make the new devices */ ugen_make_devnodes(sc); return (0); } /* retrieve a complete descriptor for a certain device and index */ static usb_config_descriptor_t * ugen_get_cdesc(struct usbd_device *udev, int index, int *lenp) { usb_config_descriptor_t *cdesc, *tdesc, cdescr; int len; if(index == USB_CURRENT_CONFIG_INDEX) { tdesc = usbd_get_config_descriptor(udev); len = UGETW(tdesc->wTotalLength); if(lenp) { *lenp = len; } cdesc = malloc(len, M_TEMP, M_WAITOK); if(cdesc == NULL) { return 0; } memcpy(cdesc, tdesc, len); PRINTFN(5,("current, len=%d\n", len)); } else { if(usbreq_get_config_desc(udev, index, &cdescr)) { return (0); } len = UGETW(cdescr.wTotalLength); PRINTFN(5,("index=%d, len=%d\n", index, len)); if(lenp) { *lenp = len; } cdesc = malloc(len, M_TEMP, M_WAITOK); if(cdesc == NULL) { return 0; } if(usbreq_get_config_desc_full(udev, index, cdesc, len)) { free(cdesc, M_TEMP); return (0); } } return (cdesc); } static int ugen_get_alt_index(struct usbd_device *udev, int ifaceidx) { struct usbd_interface *iface = usbd_get_iface(udev, ifaceidx); return (iface) ? (iface->alt_index) : -1; } static int ugenioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *p) { struct ugen_softc *sc = DEV2SC(dev); struct ugen_endpoint *sce = DEV2SCE(dev); struct usbd_interface *iface; usb_interface_descriptor_t *idesc; usb_endpoint_descriptor_t *edesc; void *data = 0; int error = 0; int len; u_int8_t conf; u_int8_t alt; PRINTFN(5, ("cmd=%08lx\n", cmd)); if((sc == NULL) || (sce == NULL)) { return (EIO); } mtx_lock(&sc->sc_mtx); if(sce->state & (UGEN_CLOSING|UGEN_GONE|UGEN_IOCTL)) { error = EIO; goto done; } switch (cmd) { case FIONBIO: /* all handled in the upper FS layer */ goto done; case USB_SET_SHORT_XFER: /* this flag only affects read */ /* check for control endpoint */ if(sce == &sc->sc_endpoints[0]) { error = EINVAL; goto done; } if(sce->pipe_in == NULL) { #ifdef USB_DEBUG printf("%s: USB_SET_SHORT_XFER, no pipe\n", __FUNCTION__); #endif error = EIO; goto done; } if(*(int *)addr) sce->state |= UGEN_SHORT_OK; else sce->state &= ~UGEN_SHORT_OK; goto done; case USB_SET_TIMEOUT: sce->in_timeout = *(int *)addr; goto done; case USB_GET_FRAME_SIZE: if(sce->pipe_in) { *(int *)addr = UGETW(sce->pipe_in->edesc->wMaxPacketSize); } else { error = EINVAL; } goto done; case USB_SET_FRAME_SIZE: if(!(flag & FWRITE)) { error = EPERM; goto done; } if((*((int *)addr) <= 0) || (*((int *)addr) >= 65536)) { error = EINVAL; goto done; } sce->out_frame_size = *(int *)addr; goto done; case USB_SET_BUFFER_SIZE: if(*(int *)addr < 1024) sce->io_buffer_size = 1024; else if(*(int *)addr < (256*1024)) sce->io_buffer_size = *(int *)addr; else sce->io_buffer_size = 256*1024; break; case USB_GET_BUFFER_SIZE: *(int *)addr = sce->io_buffer_size; break; default: break; } /* the following ioctls will sleep * and are only allowed on the * control endpoint */ if(sce != &sc->sc_endpoints[0]) { error = EINVAL; goto done; } sce->state |= UGEN_IOCTL; mtx_unlock(&sc->sc_mtx); switch (cmd) { #ifdef USB_DEBUG case USB_SETDEBUG: if(!(flag & FWRITE)) { error = EPERM; break; } usbdebug = *(int *)addr; break; #endif case USB_GET_CONFIG: error = usbreq_get_config(sc->sc_udev, &conf); if(error) { error = EIO; break; } *(int *)addr = conf; break; case USB_SET_CONFIG: if(!(flag & FWRITE)) { error = EPERM; break; } error = ugen_set_config(sc, *(int *)addr); if(error) { break; } break; case USB_GET_ALTINTERFACE: #define ai ((struct usb_alt_interface *)addr) iface = usbd_get_iface(sc->sc_udev, ai->uai_interface_index); if(!iface || !iface->idesc) { error = EINVAL; break; } ai->uai_alt_no = iface->idesc->bAlternateSetting; break; case USB_SET_ALTINTERFACE: if(!(flag & FWRITE)) { error = EPERM; break; } error = ugen_set_interface(sc, ai->uai_interface_index, ai->uai_alt_no); if(error) { break; } break; case USB_GET_NO_ALT: data = ugen_get_cdesc(sc->sc_udev, ai->uai_config_index, 0); if(data == NULL) { error = EINVAL; break; } idesc = usbd_find_idesc(data, ai->uai_interface_index, 0); if(idesc == NULL) { error = EINVAL; break; } ai->uai_alt_no = usbd_get_no_alts(data, idesc->bInterfaceNumber); #undef ai break; case USB_GET_DEVICE_DESC: if(!usbd_get_device_descriptor(sc->sc_udev)) { error = EIO; break; } *(usb_device_descriptor_t *)addr = *usbd_get_device_descriptor(sc->sc_udev); break; case USB_GET_CONFIG_DESC: #define cd ((struct usb_config_desc *)addr) data = ugen_get_cdesc(sc->sc_udev, cd->ucd_config_index, 0); if(data == NULL) { error = EINVAL; break; } cd->ucd_desc = *(usb_config_descriptor_t *)data; #undef cd break; case USB_GET_INTERFACE_DESC: #define id ((struct usb_interface_desc *)addr) data = ugen_get_cdesc(sc->sc_udev, id->uid_config_index, 0); if(data == NULL) { error = EINVAL; break; } if((id->uid_config_index == USB_CURRENT_CONFIG_INDEX) && (id->uid_alt_index == USB_CURRENT_ALT_INDEX)) alt = ugen_get_alt_index(sc->sc_udev, id->uid_interface_index); else alt = id->uid_alt_index; idesc = usbd_find_idesc(data, id->uid_interface_index, alt); if(idesc == NULL) { error = EINVAL; break; } id->uid_desc = *idesc; #undef id break; case USB_GET_ENDPOINT_DESC: #define ed ((struct usb_endpoint_desc *)addr) data = ugen_get_cdesc(sc->sc_udev, ed->ued_config_index, 0); if(data == NULL) { error = EINVAL; break; } if((ed->ued_config_index == USB_CURRENT_CONFIG_INDEX) && (ed->ued_alt_index == USB_CURRENT_ALT_INDEX)) alt = ugen_get_alt_index(sc->sc_udev, ed->ued_interface_index); else alt = ed->ued_alt_index; edesc = usbd_find_edesc(data, ed->ued_interface_index, alt, ed->ued_endpoint_index); if(edesc == NULL) { error = EINVAL; break; } ed->ued_desc = *edesc; #undef ed break; case USB_GET_FULL_DESC: #define fd ((struct usb_full_desc *)addr) data = ugen_get_cdesc(sc->sc_udev, fd->ufd_config_index, &len); if(data == NULL) { error = EINVAL; break; } if(len > fd->ufd_size) { len = fd->ufd_size; } if(fd->ufd_data == NULL) { error = EINVAL; break; } error = copyout(data, fd->ufd_data, len); #undef fd break; case USB_GET_STRING_DESC: #define si ((struct usb_string_desc *)addr) if(usbreq_get_string_desc (sc->sc_udev, si->usd_string_index, si->usd_language_id, &si->usd_desc, &len)) { error = EINVAL; break; } #undef si break; case USB_DO_REQUEST: #define ur ((struct usb_ctl_request *)addr) if(!(flag & FWRITE)) { error = EPERM; break; } /* avoid requests that would damage the bus integrity */ if(((ur->ucr_request.bmRequestType == UT_WRITE_DEVICE) && (ur->ucr_request.bRequest == UR_SET_ADDRESS)) || ((ur->ucr_request.bmRequestType == UT_WRITE_DEVICE) && (ur->ucr_request.bRequest == UR_SET_CONFIG)) || ((ur->ucr_request.bmRequestType == UT_WRITE_INTERFACE) && (ur->ucr_request.bRequest == UR_SET_INTERFACE))) { error = EINVAL; break; } len = UGETW(ur->ucr_request.wLength); if((len < 0) || (len > 32767)) { error = EINVAL; break; } if(len != 0) { if(ur->ucr_data == NULL) { error = EINVAL; break; } data = malloc(len, M_TEMP, M_WAITOK); if(data == NULL) { error = ENOMEM; break; } if(!(ur->ucr_request.bmRequestType & UT_READ)) { error = copyin(ur->ucr_data, data, len); if(error) { break; } } } if(usbd_do_request_flags (sc->sc_udev, &ur->ucr_request, data, (ur->ucr_flags & USBD_SHORT_XFER_OK), &ur->ucr_actlen, USBD_DEFAULT_TIMEOUT)) { error = EIO; break; } if((len != 0) && (ur->ucr_request.bmRequestType & UT_READ)) { error = copyout(data, ur->ucr_data, len); if(error) { break; } } #undef ur break; case USB_GET_DEVICEINFO: usbd_fill_deviceinfo(sc->sc_udev, (struct usb_device_info *)addr, 1); break; default: error = EINVAL; break; } if(data) { free(data, M_TEMP); } mtx_lock(&sc->sc_mtx); sce->state &= ~UGEN_IOCTL; if(sce->state & UGEN_CLOSING) { wakeup(sce); error = EIO; } done: mtx_unlock(&sc->sc_mtx); return (error); } static int ugenpoll(struct cdev *dev, int events, struct thread *p) { struct ugen_softc *sc = DEV2SC(dev); struct ugen_endpoint *sce = DEV2SCE(dev); int revents = 0; PRINTFN(5, ("\n")); if((sc == NULL) || (sce == NULL)) { return POLLNVAL; } if(sce == &sc->sc_endpoints[0]) { /* not control endpoint */ return 0; } mtx_lock(&sc->sc_mtx); if(sce->state & (UGEN_CLOSING|UGEN_GONE)) { goto done; } if(sce->pipe_out && (events & (POLLOUT | POLLWRNORM)) && (!(sce->state & (UGEN_WR_CFG|UGEN_WR_SLP|UGEN_WR_UIO)))) { if(ugen_open_pipe_write(sc,sce)) { /* must return, hence the error * might indicate that the device * is about to be detached */ revents = POLLNVAL; goto done; } switch (sce->pipe_out->edesc->bmAttributes & UE_XFERTYPE) { case UE_ISOCHRONOUS: if(ugen_half_empty(&sce->out_queue)) { revents |= events & (POLLOUT | POLLWRNORM); } else { selrecord(p, &sce->selinfo); } break; case UE_BULK: case UE_INTERRUPT: /* pretend that one can write data, hence * no buffering is done: */ revents |= events & (POLLOUT | POLLWRNORM); break; } } if(sce->pipe_in && (events & (POLLIN | POLLRDNORM)) && (!(sce->state & (UGEN_RD_CFG|UGEN_RD_SLP|UGEN_RD_UIO)))) { /* check that pipes are open, so that * selwakeup is called */ if(ugen_open_pipe_read(sc,sce)) { /* must return, hence the error * might indicate that the device * is about to be detached */ revents = POLLNVAL; goto done; } switch (sce->pipe_in->edesc->bmAttributes & UE_XFERTYPE) { case UE_INTERRUPT: case UE_ISOCHRONOUS: if(ugen_not_empty(&sce->in_queue)) { revents |= events & (POLLIN | POLLRDNORM); } else { selrecord(p, &sce->selinfo); } break; case UE_BULK: /* pretend that one can read data, * hence no buffering is done: */ revents |= events & (POLLIN | POLLRDNORM); break; } } done: mtx_unlock(&sc->sc_mtx); return (revents); } cdevsw_t ugen_cdevsw = { #ifdef D_VERSION .d_version = D_VERSION, #endif .d_open = ugenopen, .d_close = ugenclose, .d_read = ugenread, .d_write = ugenwrite, .d_ioctl = ugenioctl, .d_poll = ugenpoll, .d_name = "ugen", }; static devclass_t ugen_devclass; static driver_t ugen_driver = { .name = "ugen", .methods = (device_method_t []) { DEVMETHOD(device_probe, ugen_probe), DEVMETHOD(device_attach, ugen_attach), DEVMETHOD(device_detach, ugen_detach), { 0, 0 } }, .size = sizeof(struct ugen_softc), }; DRIVER_MODULE(ugen, uhub, ugen_driver, ugen_devclass, usbd_driver_load, 0); MODULE_DEPEND(ugen, usb, 1, 1, 1); pwcbsd/usb/uhci.c000644 000423 000000 00000211642 10551670754 014503 0ustar00luigiwheel000000 000000 /* $NetBSD: uhci.c,v 1.170 2003/02/19 01:35:04 augustss Exp $ */ /* Also already incorporated from NetBSD: * $NetBSD: uhci.c,v 1.172 2003/02/23 04:19:26 simonb Exp $ * $NetBSD: uhci.c,v 1.173 2003/05/13 04:41:59 gson Exp $ * $NetBSD: uhci.c,v 1.175 2003/09/12 16:18:08 mycroft Exp $ * $NetBSD: uhci.c,v 1.176 2003/11/04 19:11:21 mycroft Exp $ * $NetBSD: uhci.c,v 1.177 2003/12/29 08:17:10 toshii Exp $ * $NetBSD: uhci.c,v 1.178 2004/03/02 16:32:05 martin Exp $ * $NetBSD: uhci.c,v 1.180 2004/07/17 20:12:03 mycroft Exp $ */ #include __FBSDID("$FreeBSD: src/sys/dev/usb/uhci.c,v 1.171 2006/09/07 00:06:41 imp Exp $"); /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * USB Universal Host Controller driver. * Handles e.g. PIIX3 and PIIX4. * * UHCI spec: http://developer.intel.com/design/USB/UHCI11D.htm * USB spec: http://www.usb.org/developers/docs/usbspec.zip * PIIXn spec: ftp://download.intel.com/design/intarch/datashts/29055002.pdf * ftp://download.intel.com/design/intarch/datashts/29056201.pdf */ #include #include #include #include #include /* LIST_XXX() */ #include #include #define INCLUDE_PCIXXX_H #include #include #include #include #define MS_TO_TICKS(ms) (((ms) * hz) / 1000) #define UHCI_BUS2SC(bus) ((uhci_softc_t *)(((u_int8_t *)(bus)) - \ POINTER_TO_UNSIGNED(&(((uhci_softc_t *)0)->sc_bus)))) #ifdef USB_DEBUG #undef DPRINTF #undef DPRINTFN #define DPRINTF(x) { if (uhcidebug) { printf("%s: ", __FUNCTION__); printf x ; } } #define DPRINTFN(n,x) { if (uhcidebug > (n)) { printf("%s: ", __FUNCTION__); printf x ; } } int uhcidebug = 0; int uhcinoloop = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, uhci, CTLFLAG_RW, 0, "USB uhci"); SYSCTL_INT(_hw_usb_uhci, OID_AUTO, debug, CTLFLAG_RW, &uhcidebug, 0, "uhci debug level"); SYSCTL_INT(_hw_usb_uhci, OID_AUTO, loop, CTLFLAG_RW, &uhcinoloop, 0, "uhci noloop"); static void uhci_dumpregs(uhci_softc_t *sc); static void uhci_dump_tds(uhci_td_t *td); #endif /* NOTE: ``(sc)->ios'' is not setup */ #define UBARR(sc) bus_space_barrier((sc)->iot, (sc)->ioh, 0, (sc)->ios, \ BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE) #define UWRITE1(sc, r, x) \ do { UBARR(sc); bus_space_write_1((sc)->iot, (sc)->ioh, (r), (x)); \ } while (/*CONSTCOND*/0) #define UWRITE2(sc, r, x) \ do { UBARR(sc); bus_space_write_2((sc)->iot, (sc)->ioh, (r), (x)); \ } while (/*CONSTCOND*/0) #define UWRITE4(sc, r, x) \ do { UBARR(sc); bus_space_write_4((sc)->iot, (sc)->ioh, (r), (x)); \ } while (/*CONSTCOND*/0) #define UREAD1(sc, r) (UBARR(sc), bus_space_read_1((sc)->iot, (sc)->ioh, (r))) #define UREAD2(sc, r) (UBARR(sc), bus_space_read_2((sc)->iot, (sc)->ioh, (r))) #define UREAD4(sc, r) (UBARR(sc), bus_space_read_4((sc)->iot, (sc)->ioh, (r))) #define UHCICMD(sc, cmd) UWRITE2(sc, UHCI_CMD, cmd) #define UHCISTS(sc) UREAD2(sc, UHCI_STS) #define UHCI_RESET_TIMEOUT 100 /* ms, reset timeout */ #define UHCI_INTR_ENDPT 1 #define ADD_BYTES(ptr,size) ((void *)(((u_int8_t *)(ptr)) + (size))) extern struct usbd_bus_methods uhci_bus_methods; extern struct usbd_pipe_methods uhci_device_bulk_methods; extern struct usbd_pipe_methods uhci_device_ctrl_methods; extern struct usbd_pipe_methods uhci_device_intr_methods; extern struct usbd_pipe_methods uhci_device_isoc_methods; extern struct usbd_pipe_methods uhci_root_ctrl_methods; extern struct usbd_pipe_methods uhci_root_intr_methods; void uhci_reset(uhci_softc_t *sc) { u_int n; mtx_assert(&sc->sc_bus.mtx, MA_OWNED); DPRINTF(("resetting the HC\n")); /* disable interrupts */ UWRITE2(sc, UHCI_INTR, 0); /* global reset */ UHCICMD(sc, UHCI_CMD_GRESET); /* wait */ DELAY(1000*USB_BUS_RESET_DELAY); /* terminate all transfers */ UHCICMD(sc, UHCI_CMD_HCRESET); /* the reset bit goes low when the controller is done */ n = UHCI_RESET_TIMEOUT; while(n--) { /* wait one millisecond */ DELAY(1000); if(!(UREAD2(sc, UHCI_CMD) & UHCI_CMD_HCRESET)) { goto done_1; } } device_printf(sc->sc_bus.bdev, "controller did not reset\n"); done_1: n = 10; while(n--) { /* wait one millisecond */ DELAY(1000); /* check if HC is stopped */ if(UREAD2(sc, UHCI_STS) & UHCI_STS_HCH) { goto done_2; } } device_printf(sc->sc_bus.bdev, "controller did not stop\n"); done_2: /* reload the configuration */ UWRITE4(sc, UHCI_FLBASEADDR, sc->sc_physaddr); UWRITE2(sc, UHCI_FRNUM, sc->sc_saved_frnum); UWRITE1(sc, UHCI_SOF, sc->sc_saved_sof); return; } static void uhci_start(uhci_softc_t *sc) { mtx_assert(&sc->sc_bus.mtx, MA_OWNED); DPRINTFN(1,("enabling\n")); /* enable interrupts */ UWRITE2(sc, UHCI_INTR, (UHCI_INTR_TOCRCIE| UHCI_INTR_RIE| UHCI_INTR_IOCE| UHCI_INTR_SPIE)); /* assume 64 byte packets at frame end and * start HC controller */ UHCICMD(sc, (UHCI_CMD_MAXP|UHCI_CMD_RS)); u_int8_t n = 10; while(n--) { /* wait one millisecond */ DELAY(1000); /* check that controller has started */ if(!(UREAD2(sc, UHCI_STS) & UHCI_STS_HCH)) { goto done; } } device_printf(sc->sc_bus.bdev, "cannot start HC controller\n"); done: return; } #define PHYSADDR(sc,what) \ ((sc)->sc_physaddr + POINTER_TO_UNSIGNED(&(((struct uhci_softc *)0)->what))) usbd_status uhci_init(uhci_softc_t *sc) { u_int16_t bit; u_int16_t x; u_int16_t y; mtx_lock(&sc->sc_bus.mtx); DPRINTF(("start\n")); #ifdef USB_DEBUG if(uhcidebug > 2) { uhci_dumpregs(sc); } #endif sc->sc_saved_sof = 0x40; /* default value */ sc->sc_saved_frnum = 0; /* default frame number */ /* * setup self pointers */ sc->sc_hw.ls_ctl_start.qh_self = htole32(PHYSADDR(sc,sc_hw.ls_ctl_start)|UHCI_PTR_QH); sc->sc_hw.hs_ctl_start.qh_self = htole32(PHYSADDR(sc,sc_hw.hs_ctl_start)|UHCI_PTR_QH); sc->sc_hw.bulk_start.qh_self = htole32(PHYSADDR(sc,sc_hw.bulk_start)|UHCI_PTR_QH); sc->sc_hw.last_qh.qh_self = htole32(PHYSADDR(sc,sc_hw.last_qh)|UHCI_PTR_QH); sc->sc_hw.last_td.td_self = htole32(PHYSADDR(sc,sc_hw.last_td)|UHCI_PTR_TD); for(x = 0; x < UHCI_VFRAMELIST_COUNT; x++) { sc->sc_hw.isoc_start[x].td_self = htole32(PHYSADDR(sc,sc_hw.isoc_start[x])|UHCI_PTR_TD); sc->sc_isoc_p_last[x] = &sc->sc_hw.isoc_start[x]; } for(x = 0; x < UHCI_IFRAMELIST_COUNT; x++) { sc->sc_hw.intr_start[x].qh_self = htole32(PHYSADDR(sc,sc_hw.intr_start[x])|UHCI_PTR_QH); sc->sc_intr_p_last[x] = &sc->sc_hw.intr_start[x]; } /* * the QHs are arranged to give poll intervals that are * powers of 2 times 1ms */ bit = UHCI_IFRAMELIST_COUNT/2; while(bit) { x = bit; while(x & bit) { y = (x ^ bit)|(bit/2); /* the next QH has half the * poll interval */ sc->sc_hw.intr_start[x].h_next = NULL; sc->sc_hw.intr_start[x].qh_h_next = sc->sc_hw.intr_start[y].qh_self; sc->sc_hw.intr_start[x].e_next = NULL; sc->sc_hw.intr_start[x].qh_e_next = htole32(UHCI_PTR_T); x++; } bit >>= 1; } /* start QH for interrupt traffic */ sc->sc_hw.intr_start[0].h_next = &sc->sc_hw.ls_ctl_start; sc->sc_hw.intr_start[0].qh_h_next = sc->sc_hw.ls_ctl_start.qh_self; sc->sc_hw.intr_start[0].e_next = 0; sc->sc_hw.intr_start[0].qh_e_next = htole32(UHCI_PTR_T); for(x = 0; x < UHCI_VFRAMELIST_COUNT; x++) { /* start TD for isochronous traffic */ sc->sc_hw.isoc_start[x].next = NULL; sc->sc_hw.isoc_start[x].td_next = sc->sc_hw.intr_start[x|(UHCI_IFRAMELIST_COUNT/2)].qh_self; sc->sc_hw.isoc_start[x].td_status = htole32(UHCI_TD_IOS); sc->sc_hw.isoc_start[x].td_token = htole32(0); sc->sc_hw.isoc_start[x].td_buffer = htole32(0); } /* start QH where low speed control traffic will be queued */ sc->sc_hw.ls_ctl_start.h_next = &sc->sc_hw.hs_ctl_start; sc->sc_hw.ls_ctl_start.qh_h_next = sc->sc_hw.hs_ctl_start.qh_self; sc->sc_hw.ls_ctl_start.e_next = 0; sc->sc_hw.ls_ctl_start.qh_e_next = htole32(UHCI_PTR_T); sc->sc_ls_ctl_p_last = &sc->sc_hw.ls_ctl_start; /* start QH where high speed control traffic will be queued */ sc->sc_hw.hs_ctl_start.h_next = &sc->sc_hw.bulk_start; sc->sc_hw.hs_ctl_start.qh_h_next = sc->sc_hw.bulk_start.qh_self; sc->sc_hw.hs_ctl_start.e_next = 0; sc->sc_hw.hs_ctl_start.qh_e_next = htole32(UHCI_PTR_T); sc->sc_hs_ctl_p_last = &sc->sc_hw.hs_ctl_start; /* start QH where bulk traffic will be queued */ sc->sc_hw.bulk_start.h_next = &sc->sc_hw.last_qh; sc->sc_hw.bulk_start.qh_h_next = sc->sc_hw.last_qh.qh_self; sc->sc_hw.bulk_start.e_next = 0; sc->sc_hw.bulk_start.qh_e_next = htole32(UHCI_PTR_T); sc->sc_bulk_p_last = &sc->sc_hw.bulk_start; /* end QH which is used for looping the QHs */ sc->sc_hw.last_qh.h_next = 0; sc->sc_hw.last_qh.qh_h_next = htole32(UHCI_PTR_T); /* end of QH chain */ sc->sc_hw.last_qh.e_next = &sc->sc_hw.last_td; sc->sc_hw.last_qh.qh_e_next = sc->sc_hw.last_td.td_self; /* end TD which hangs from the last QH, * to avoid a bug in the PIIX that * makes it run berserk otherwise */ sc->sc_hw.last_td.next = 0; sc->sc_hw.last_td.td_next = htole32(UHCI_PTR_T); sc->sc_hw.last_td.td_status = htole32(0); /* inactive */ sc->sc_hw.last_td.td_token = htole32(0); sc->sc_hw.last_td.td_buffer = htole32(0); /* setup UHCI framelist */ for(x = 0; x < UHCI_FRAMELIST_COUNT; x++) { sc->sc_hw.pframes[x] = sc->sc_hw.isoc_start[x % UHCI_VFRAMELIST_COUNT].td_self; } LIST_INIT(&sc->sc_interrupt_list_head); /* set up the bus struct */ sc->sc_bus.methods = &uhci_bus_methods; /* reset the controller */ uhci_reset(sc); /* start the controller */ uhci_start(sc); mtx_unlock(&sc->sc_bus.mtx); return 0; } /* NOTE: suspend/resume is called from * interrupt context and cannot sleep! */ void uhci_suspend(uhci_softc_t *sc) { mtx_lock(&sc->sc_bus.mtx); #ifdef USB_DEBUG if(uhcidebug > 2) { uhci_dumpregs(sc); } #endif /* save some state if BIOS doesn't */ sc->sc_saved_frnum = UREAD2(sc, UHCI_FRNUM); sc->sc_saved_sof = UREAD1(sc, UHCI_SOF); /* stop the controller */ uhci_reset(sc); /* enter global suspend */ UHCICMD(sc, UHCI_CMD_EGSM); DELAY(1000*USB_RESUME_WAIT); mtx_unlock(&sc->sc_bus.mtx); return; } void uhci_resume(uhci_softc_t *sc) { mtx_lock(&sc->sc_bus.mtx); /* reset the controller */ uhci_reset(sc); /* force global resume */ UHCICMD(sc, UHCI_CMD_FGR); DELAY(1000*USB_RESUME_DELAY); /* and start traffic again */ uhci_start(sc); #ifdef USB_DEBUG if(uhcidebug > 2) { uhci_dumpregs(sc); } #endif mtx_unlock(&sc->sc_bus.mtx); return; } #ifdef USB_DEBUG static void uhci_dumpregs(uhci_softc_t *sc) { DPRINTFN(-1,("%s regs: cmd=%04x, sts=%04x, intr=%04x, frnum=%04x, " "flbase=%08x, sof=%04x, portsc1=%04x, portsc2=%04x\n", device_get_nameunit(sc->sc_bus.bdev), UREAD2(sc, UHCI_CMD), UREAD2(sc, UHCI_STS), UREAD2(sc, UHCI_INTR), UREAD2(sc, UHCI_FRNUM), UREAD4(sc, UHCI_FLBASEADDR), UREAD1(sc, UHCI_SOF), UREAD2(sc, UHCI_PORTSC1), UREAD2(sc, UHCI_PORTSC2))); return; } static void uhci_dump_td(uhci_td_t *p) { u_int32_t td_next = le32toh(p->td_next); u_int32_t td_status = le32toh(p->td_status); printf("TD(%p) at %08lx = link=0x%08lx status=0x%08lx " "token=0x%08lx buffer=0x%08lx\n", p, (long)le32toh(p->td_self), (long)le32toh(p->td_next), (long)le32toh(p->td_status), (long)le32toh(p->td_token), (long)le32toh(p->td_buffer)); printf("TD(%p) td_next=%s%s%s td_status=%s%s%s%s%s%s%s%s%s%s%s, errcnt=%d, actlen=%d pid=%02x," "addr=%d,endpt=%d,D=%d,maxlen=%d\n", p, (td_next & 1) ? "-T" : "", (td_next & 2) ? "-Q" : "", (td_next & 4) ? "-VF" : "", (td_status & UHCI_TD_BITSTUFF) ? "-BITSTUFF" : "", (td_status & UHCI_TD_CRCTO) ? "-CRCTO" : "", (td_status & UHCI_TD_NAK) ? "-NAK" : "", (td_status & UHCI_TD_BABBLE) ? "-BABBLE" : "", (td_status & UHCI_TD_DBUFFER) ? "-DBUFFER" : "", (td_status & UHCI_TD_STALLED) ? "-STALLED" : "", (td_status & UHCI_TD_ACTIVE) ? "-ACTIVE" : "", (td_status & UHCI_TD_IOC) ? "-IOC" : "", (td_status & UHCI_TD_IOS) ? "-IOS" : "", (td_status & UHCI_TD_LS) ? "-LS" : "", (td_status & UHCI_TD_SPD) ? "-SPD" : "", UHCI_TD_GET_ERRCNT(le32toh(p->td_status)), UHCI_TD_GET_ACTLEN(le32toh(p->td_status)), UHCI_TD_GET_PID(le32toh(p->td_token)), UHCI_TD_GET_DEVADDR(le32toh(p->td_token)), UHCI_TD_GET_ENDPT(le32toh(p->td_token)), UHCI_TD_GET_DT(le32toh(p->td_token)), UHCI_TD_GET_MAXLEN(le32toh(p->td_token))); return; } static void uhci_dump_qh(uhci_qh_t *sqh) { DPRINTFN(-1,("QH(%p) at 0x%08x: h_next=%08x e_next=%08x\n", sqh, le32toh(sqh->qh_self), le32toh(sqh->qh_h_next), le32toh(sqh->qh_e_next))); return; } static void uhci_dump_all(uhci_softc_t *sc) { uhci_dumpregs(sc); printf("intrs=%d\n", sc->sc_bus.no_intrs); uhci_dump_qh(&sc->sc_hw.ls_ctl_start); uhci_dump_qh(&sc->sc_hw.hs_ctl_start); uhci_dump_qh(&sc->sc_hw.bulk_start); uhci_dump_qh(&sc->sc_hw.last_qh); return; } static void uhci_dump_qhs(uhci_qh_t *sqh) { uhci_dump_qh(sqh); /* uhci_dump_qhs displays all the QHs and TDs from the given QH onwards * Traverses sideways first, then down. * * QH1 * QH2 * No QH * TD2.1 * TD2.2 * TD1.1 * etc. * * TD2.x being the TDs queued at QH2 and QH1 being referenced from QH1. */ if ((sqh->h_next != NULL) && !(sqh->qh_h_next & htole32(UHCI_PTR_T))) uhci_dump_qhs(sqh->h_next); else DPRINTF(("No QH\n")); if ((sqh->e_next != NULL) && !(sqh->qh_e_next & htole32(UHCI_PTR_T))) uhci_dump_tds(sqh->e_next); else DPRINTF(("No TD\n")); return; } static void uhci_dump_tds(uhci_td_t *td) { for(; td != NULL; td = td->obj_next) { uhci_dump_td(td); /* Check whether the link pointer in this TD marks * the link pointer as end of queue. This avoids * printing the free list in case the queue/TD has * already been moved there (seatbelt). */ if ((td->td_next & htole32(UHCI_PTR_T)) || (td->td_next == 0)) { break; } } return; } #if 0 static void uhci_dump_iis(struct uhci_softc *sc) { struct usbd_xfer *xfer; printf("interrupt list:\n"); LIST_FOREACH(xfer, &sc->sc_interrupt_list_head, interrupt_list) { usbd_dump_xfer(xfer); } return; } #endif #endif /* * Let the last QH loop back to the high speed control transfer QH. * This is what intel calls "bandwidth reclamation" and improves * USB performance a lot for some devices. * If we are already looping, just count it. */ static void uhci_add_loop(uhci_softc_t *sc) { #ifdef USB_DEBUG if(uhcinoloop) { return; } #endif if(++sc->sc_loops == 1) { DPRINTFN(5,("add\n")); /* NOTE: we don't loop back the soft pointer */ #if 0 sc->sc_hw.last_qh.qh_h_next = sc->sc_hw.hs_ctl_start.qh_self; #else sc->sc_hw.last_qh.qh_h_next = sc->sc_hw.bulk_start.qh_self; #endif } return; } static void uhci_rem_loop(uhci_softc_t *sc) { #ifdef USB_DEBUG if(uhcinoloop) { return; } #endif if(--sc->sc_loops == 0) { DPRINTFN(5,("remove\n")); sc->sc_hw.last_qh.qh_h_next = htole32(UHCI_PTR_T); } return; } #define UHCI_APPEND_TD(std,last) (last) = _uhci_append_td(std,last) static uhci_td_t * _uhci_append_td(uhci_td_t *std, uhci_td_t *last) { DPRINTFN(10, ("%p to %p\n", std, last)); /* (sc->sc_bus.mtx) must be locked */ std->next = last->next; std->td_next = last->td_next; std->prev = last; /* the last->next->prev is never followed: * std->next->prev = std; */ last->next = std; last->td_next = std->td_self; return(std); } #define UHCI_APPEND_QH(sqh,last) (last) = _uhci_append_qh(sqh,last) static uhci_qh_t * _uhci_append_qh(uhci_qh_t *sqh, uhci_qh_t *last) { DPRINTFN(10, ("%p to %p\n", sqh, last)); /* (sc->sc_bus.mtx) must be locked */ sqh->h_next = last->h_next; sqh->qh_h_next = last->qh_h_next; sqh->h_prev = last; /* the last->h_next->h_prev is never followed: * sqh->h_next->h_prev = sqh; */ last->h_next = sqh; last->qh_h_next = sqh->qh_self; return(sqh); } /**/ #define UHCI_REMOVE_TD(std,last) (last) = _uhci_remove_td(std,last) static uhci_td_t * _uhci_remove_td(uhci_td_t *std, uhci_td_t *last) { DPRINTFN(10, ("%p from %p\n", std, last)); /* (sc->sc_bus.mtx) must be locked */ std->prev->next = std->next; std->prev->td_next = std->td_next; if(std->next) { std->next->prev = std->prev; } return((last == std) ? std->prev : last); } #define UHCI_REMOVE_QH(sqh,last) (last) = _uhci_remove_qh(sqh,last) static uhci_qh_t * _uhci_remove_qh(uhci_qh_t *sqh, uhci_qh_t *last) { DPRINTFN(10, ("%p from %p\n", sqh, last)); /* (sc->sc_bus.mtx) must be locked */ /* only remove if not removed from a queue */ if(sqh->h_prev) { sqh->h_prev->h_next = sqh->h_next; sqh->h_prev->qh_h_next = sqh->qh_h_next; if(sqh->h_next) { sqh->h_next->h_prev = sqh->h_prev; } /* set the Terminate-bit in the e_next of the QH, * in case the transferred packet was short so * that the QH still points at the last used TD */ sqh->qh_e_next = htole32(UHCI_PTR_T); last = ((last == sqh) ? sqh->h_prev : last); sqh->h_prev = 0; } return(last); } static void uhci_device_done(struct usbd_xfer *xfer, usbd_status error); static u_int8_t uhci_isoc_done(uhci_softc_t *sc, struct usbd_xfer *xfer) { struct usbd_page_search buf_res; u_int32_t nframes = xfer->nframes; u_int32_t actlen = 0; u_int16_t *plen = xfer->frlengths; u_int16_t len = 0; u_int16_t temp; u_int8_t need_delay = 0; uhci_td_t *td = xfer->td_transfer_first; uhci_td_t **pp_last = &sc->sc_isoc_p_last[xfer->qh_pos]; DPRINTFN(12, ("xfer=%p pipe=%p transfer done\n", xfer, xfer->pipe)); while(nframes--) { if(td == NULL) { panic("%s:%d: out of TD's\n", __FUNCTION__, __LINE__); } if(pp_last >= &sc->sc_isoc_p_last[UHCI_VFRAMELIST_COUNT]) { pp_last = &sc->sc_isoc_p_last[0]; } #ifdef USB_DEBUG if(uhcidebug > 5) { DPRINTFN(-1,("isoc TD\n")); uhci_dump_td(td); } #endif /* check for active transfers */ if(td->td_status & htole32(UHCI_TD_ACTIVE)) { need_delay = 1; } len = UHCI_TD_GET_ACTLEN(le32toh(td->td_status)); if(len > *plen) { len = *plen; } *plen = len; actlen += len; if (td->fixup_ptr) { usbd_get_page(&(xfer->buf_data), td->fixup_offset, &buf_res); temp = min(buf_res.length, len); bcopy(td->fixup_ptr, buf_res.buffer, temp); len -= temp; if (len) { usbd_get_page(&(xfer->buf_data), td->fixup_offset + temp, &buf_res); bcopy(ADD_BYTES(td->fixup_ptr, temp), buf_res.buffer, len); } } /* remove TD from schedule */ UHCI_REMOVE_TD(td, *pp_last); pp_last++; plen++; td = td->obj_next; } xfer->actlen = actlen; return need_delay; } static void uhci_non_isoc_done(struct usbd_xfer *xfer) { u_int32_t status = 0; u_int32_t actlen = 0; uhci_td_t *td = xfer->td_transfer_first; DPRINTFN(12, ("xfer=%p pipe=%p transfer done\n", xfer, xfer->pipe)); #ifdef USB_DEBUG if(uhcidebug > 10) { uhci_dump_tds(td); } #endif if(!(td->td_status & htole32(UHCI_TD_ACTIVE)) && (td->td_status & htole32(UHCI_TD_STALLED | UHCI_TD_NAK)) && (UHCI_TD_GET_PID(le32toh(td->td_token)) == UHCI_TD_PID_SETUP)) { /* * UHCI will report CRCTO in addition to a STALL or NAK * for a SETUP transaction. See section 3.2.2, "TD * CONTROL AND STATUS" */ td->td_status &= htole32(~UHCI_TD_CRCTO); } /* the transfer is done, compute actual length and status */ for (; td != NULL; td = ((td == xfer->td_transfer_last) ? NULL : td->obj_next)) { if(td->td_status & htole32(UHCI_TD_ACTIVE)) { break; } status = le32toh(td->td_status); actlen += UHCI_TD_GET_ACTLEN(status); } /* if there are left over TDs * the toggle needs to be updated */ if(td != NULL) { xfer->pipe->toggle_next = (td->td_token & htole32(UHCI_TD_SET_DT(1))) ? 1 : 0; } DPRINTFN(10, ("actlen=%d\n", actlen)); xfer->actlen = actlen; status &= UHCI_TD_ERROR; #ifdef USB_DEBUG if(status == UHCI_TD_STALLED) { DPRINTFN(10, ("error, addr=%d, endpt=0x%02x, " "status=%s%s%s%s%s%s%s%s%s%s%s\n", xfer->address, xfer->endpoint, (status & UHCI_TD_BITSTUFF) ? "-BITSTUFF" : "", (status & UHCI_TD_CRCTO) ? "-CRCTO" : "", (status & UHCI_TD_NAK) ? "-NAK" : "", (status & UHCI_TD_BABBLE) ? "-BABBLE" : "", (status & UHCI_TD_DBUFFER) ? "-DBUFFER" : "", (status & UHCI_TD_STALLED) ? "-STALLED" : "", (status & UHCI_TD_ACTIVE) ? "-ACTIVE" : "", (status & UHCI_TD_IOC) ? "-IOC" : "", (status & UHCI_TD_IOS) ? "-IOS" : "", (status & UHCI_TD_LS) ? "-LS" : "", (status & UHCI_TD_SPD) ? "-SPD" : "")); } #endif uhci_device_done(xfer, (status == 0) ? USBD_NORMAL_COMPLETION : (status == UHCI_TD_STALLED) ? USBD_STALLED : USBD_IOERROR); return; } /* returns one when transfer is finished * and callback must be called; else zero */ static u_int8_t uhci_check_transfer(struct usbd_xfer *xfer, struct thread *ctd) { uhci_td_t *td; DPRINTFN(15, ("xfer=%p\n", xfer)); if(xfer->usb_thread != ctd) { /* cannot call this transfer * back due to locking ! */ goto done; } td = xfer->td_transfer_last; if(xfer->pipe->methods == &uhci_device_isoc_methods) { /* isochronous transfer */ if(!(td->td_status & htole32(UHCI_TD_ACTIVE))) { uhci_device_done(xfer,USBD_NORMAL_COMPLETION); goto transferred; } } else { /* non-isochronous transfer */ if(td->td_status & htole32(UHCI_TD_ACTIVE)) { /* * if the last TD is still active we need to * check whether there is an error somewhere * in the middle, or whether there was a short * packet (SPD and not ACTIVE) */ DPRINTFN(12, ("xfer=%p active\n", xfer)); for(td = xfer->td_transfer_cache; td != NULL; td = ((td == xfer->td_transfer_last) ? NULL : td->obj_next)) { u_int32_t status; status = le32toh(td->td_status); /* if there's an active TD * the transfer isn't done */ if(status & UHCI_TD_ACTIVE) { DPRINTFN(12, ("xfer=%p is still " "active\n", xfer)); /* update cache */ xfer->td_transfer_cache = td; goto done; } /* any kind of error makes * the transfer done */ if(status & UHCI_TD_STALLED) { break; } /* we want short packets, * and if it is short: it's done */ if((status & UHCI_TD_SPD) && (UHCI_TD_GET_ACTLEN(status) < UHCI_TD_GET_MAXLEN(le32toh(td->td_token)))) { break; } } } uhci_non_isoc_done(xfer); goto transferred; } done: return 0; transferred: return 1; } static void uhci_interrupt_td(uhci_softc_t *sc, struct thread *ctd) { enum { FINISH_LIST_MAX = 16 }; struct usbd_callback_info info[FINISH_LIST_MAX]; struct usbd_callback_info *ptr = &info[0]; struct usbd_xfer *xfer; u_int32_t status; u_int8_t need_repeat = 0; mtx_lock(&sc->sc_bus.mtx); /* * It can happen that an interrupt will be delivered to * us before the device has been fully attached and the * softc struct has been configured. Usually this happens * when kldloading the USB support as a module after the * system has been booted. If we detect this condition, * we need to squelch the unwanted interrupts until we're * ready for them. */ if(sc->sc_bus.bdev == NULL) /* XXX */ { #if 0 UWRITE2(sc, UHCI_STS, 0xFFFF); /* ack pending interrupts */ uhci_reset(sc); /* stop the controller */ UWRITE2(sc, UHCI_INTR, 0); /* disable interrupts */ #endif goto done; } if(ctd) { /* the poll thread should not read * any status registers that will * clear interrupts! */ goto repeat; } sc->sc_bus.no_intrs++; DPRINTFN(15,("%s: real interrupt\n", device_get_nameunit(sc->sc_bus.bdev))); #ifdef USB_DEBUG if(uhcidebug > 15) { DPRINTF(("%s\n", device_get_nameunit(sc->sc_bus.bdev))); uhci_dumpregs(sc); } #endif status = UREAD2(sc, UHCI_STS) & UHCI_STS_ALLINTRS; if(status == 0) { /* the interrupt was not for us */ goto done; } if(status & UHCI_STS_RD) { #ifdef USB_DEBUG device_printf(sc->sc_bus.bdev, "resume detect\n"); #endif } if(status & UHCI_STS_HSE) { device_printf(sc->sc_bus.bdev, "host system error\n"); } if(status & UHCI_STS_HCPE) { device_printf(sc->sc_bus.bdev, "host controller process error\n"); } if(status & UHCI_STS_HCH) { /* no acknowledge needed */ device_printf(sc->sc_bus.bdev, "host controller halted\n"); #ifdef USB_DEBUG uhci_dump_all(sc); #endif } /* get acknowledge bits */ status &= (UHCI_STS_USBINT| UHCI_STS_USBEI| UHCI_STS_RD| UHCI_STS_HSE| UHCI_STS_HCPE); if(status == 0) { /* nothing to acknowledge */ goto done; } /* acknowledge interrupts */ UWRITE2(sc, UHCI_STS, status); /* * when the host controller interrupts because a transfer * is completed, all active transfers must be checked! * The UHCI controller does not have a queue for finished * transfers */ repeat: LIST_FOREACH(xfer, &sc->sc_interrupt_list_head, interrupt_list) { /* check if transfer is * transferred */ if(uhci_check_transfer(xfer, ctd)) { /* queue callback */ ptr->xfer = xfer; ptr->refcount = xfer->usb_refcount; ptr++; xfer->usb_root->memory_refcount++; /* check queue length */ if(ptr >= &info[FINISH_LIST_MAX]) { need_repeat = 1; break; } } } done: mtx_unlock(&sc->sc_bus.mtx); usbd_do_callback(&info[0],ptr); if(need_repeat) { ptr = &info[0]; need_repeat = 0; mtx_lock(&sc->sc_bus.mtx); goto repeat; } return; } void uhci_interrupt(uhci_softc_t *sc) { uhci_interrupt_td(sc, NULL); return; } /* * called when a request does not complete */ static void uhci_timeout(struct usbd_xfer *xfer) { struct usbd_callback_info info[1]; uhci_softc_t *sc = xfer->usb_sc; DPRINTF(("xfer=%p\n", xfer)); mtx_assert(&sc->sc_bus.mtx, MA_OWNED); /* transfer is transferred */ uhci_device_done(xfer, USBD_TIMEOUT); /* queue callback */ info[0].xfer = xfer; info[0].refcount = xfer->usb_refcount; xfer->usb_root->memory_refcount++; mtx_unlock(&sc->sc_bus.mtx); usbd_do_callback(&info[0],&info[1]); return; } static void uhci_do_poll(struct usbd_bus *bus) { uhci_interrupt_td(UHCI_BUS2SC(bus), curthread); return; } #define uhci_add_interrupt_info(sc, xfer) \ LIST_INSERT_HEAD(&(sc)->sc_interrupt_list_head, (xfer), interrupt_list) static void uhci_remove_interrupt_info(struct usbd_xfer *xfer) { if((xfer)->interrupt_list.le_prev) { LIST_REMOVE((xfer), interrupt_list); (xfer)->interrupt_list.le_prev = NULL; } return; } static uhci_td_t * uhci_setup_standard_chain(struct usbd_xfer *xfer) { struct usbd_page_search buf_res; u_int32_t td_status; u_int32_t td_token; u_int32_t average = xfer->max_packet_size; u_int32_t buf_offset; u_int32_t len = xfer->length; u_int8_t isread; u_int8_t shortpkt = 0; uhci_td_t *td; uhci_td_t *td_last = NULL; DPRINTFN(8, ("addr=%d endpt=%d len=%d speed=%d\n", xfer->address, UE_GET_ADDR(xfer->endpoint), xfer->length, xfer->udev->speed)); td = (xfer->td_transfer_first = xfer->td_transfer_cache = xfer->td_start); buf_offset = 0; usbd_get_page(&(xfer->buf_data), buf_offset, &buf_res); td_status = htole32(UHCI_TD_ZERO_ACTLEN(UHCI_TD_SET_ERRCNT(3)| UHCI_TD_ACTIVE)); if(xfer->udev->speed == USB_SPEED_LOW) { td_status |= htole32(UHCI_TD_LS); } if(xfer->flags & USBD_SHORT_XFER_OK) { /* set UHCI_TD_SPD */ td_status |= htole32(UHCI_TD_SPD); } if(xfer->pipe->methods == &uhci_device_ctrl_methods) { /* the first byte is "bmRequestType" */ isread = *((u_int8_t *)(buf_res.buffer)); isread &= UT_READ; /* * check length ? */ xfer->pipe->toggle_next = 1; /* SETUP message */ td->td_status = td_status & htole32(~UHCI_TD_SPD); td->td_token = htole32(UHCI_TD_SET_ENDPT(xfer->endpoint)) | htole32(UHCI_TD_SET_DEVADDR(xfer->address)) | htole32(UHCI_TD_SET_MAXLEN(sizeof(usb_device_request_t))) | htole32(UHCI_TD_PID_SETUP)| htole32(UHCI_TD_SET_DT(0)); td->td_buffer = htole32(buf_res.physaddr); buf_offset += sizeof(usb_device_request_t); usbd_get_page(&(xfer->buf_data), buf_offset, &buf_res); len -= sizeof(usb_device_request_t); td_last = td; td = td->obj_next; } else { isread = (UE_GET_DIR(xfer->endpoint) == UE_DIR_IN); if(xfer->length == 0) { /* must allow access to "td_last", * so xfer->length cannot be zero! */ printf("%s: setting USBD_FORCE_SHORT_XFER!\n", __FUNCTION__); xfer->flags |= USBD_FORCE_SHORT_XFER; } } td_token = htole32(UHCI_TD_SET_ENDPT(xfer->endpoint)) | htole32(UHCI_TD_SET_DEVADDR(xfer->address)) | htole32(UHCI_TD_SET_MAXLEN(average)) | (isread ? htole32(UHCI_TD_PID_IN) : htole32(UHCI_TD_PID_OUT)); if(xfer->pipe->toggle_next) { td_token |= htole32(UHCI_TD_SET_DT(1)); } while(1) { if(len == 0) { if(xfer->flags & USBD_FORCE_SHORT_XFER) { if(shortpkt) { break; } } else { break; } } if(len < average) { shortpkt = 1; average = len; /* update length */ td_token &= htole32(~(0x7ff<<21)); td_token |= htole32(UHCI_TD_SET_MAXLEN(average)); } if(td == NULL) { panic("%s: software wants to write more data " "than there is in the buffer!", __FUNCTION__); } /* link in last TD */ if (td_last) { td_last->td_next = td->td_self; } /* fill out current TD */ td->td_status = td_status; td->td_token = td_token; td->td_buffer = htole32(buf_res.physaddr); td_token ^= htole32(UHCI_TD_SET_DT(1)); buf_offset += average; usbd_get_page(&(xfer->buf_data), buf_offset, &buf_res); len -= average; td_last = td; td = td->obj_next; } /* set interrupt bit */ td_status |= htole32(UHCI_TD_IOC); if(xfer->pipe->methods == &uhci_device_ctrl_methods) { /* link in last TD */ td_last->td_next = td->td_self; /* STATUS message */ /* update length and PID */ td_token &= htole32(~(0x7ff<<21)); td_token |= htole32(UHCI_TD_SET_MAXLEN(0)|UHCI_TD_SET_DT(1)); td_token ^= htole32(0x88); td->td_token = td_token; td->td_buffer = htole32(0); td_last = td; } td_last->td_next = htole32(UHCI_PTR_T); td_last->td_status = td_status; /* must have at least one frame! */ xfer->td_transfer_last = td_last; /* store data-toggle */ if(td_token & htole32(UHCI_TD_SET_DT(1))) { xfer->pipe->toggle_next = 1; } else { xfer->pipe->toggle_next = 0; } #ifdef USB_DEBUG if(uhcidebug > 8) { DPRINTF(("nexttog=%d; data before transfer:\n", xfer->pipe->toggle_next)); uhci_dump_tds(xfer->td_start); } #endif return(xfer->td_start); } /* NOTE: "done" can be run two times in a row, * from close and from interrupt */ static void uhci_device_done(struct usbd_xfer *xfer, usbd_status error) { uhci_softc_t *sc = xfer->usb_sc; uhci_td_t *td; uhci_qh_t *qh; u_int8_t need_delay; mtx_assert(&sc->sc_bus.mtx, MA_OWNED); need_delay = 0; DPRINTFN(1,("xfer=%p, pipe=%p length=%d error=%d\n", xfer, xfer->pipe, xfer->actlen, error)); for(qh = xfer->qh_start; qh; qh = qh->obj_next) { if(!(qh->qh_e_next & htole32(UHCI_PTR_T))) { need_delay = 1; } qh->e_next = 0; qh->qh_e_next = htole32(UHCI_PTR_T); } if(xfer->flags & USBD_BANDWIDTH_RECLAIMED) { xfer->flags &= ~USBD_BANDWIDTH_RECLAIMED; uhci_rem_loop(sc); } if(xfer->pipe->methods == &uhci_device_bulk_methods) { UHCI_REMOVE_QH(xfer->qh_start, sc->sc_bulk_p_last); } if(xfer->pipe->methods == &uhci_device_ctrl_methods) { if(xfer->udev->speed == USB_SPEED_LOW) { UHCI_REMOVE_QH(xfer->qh_start, sc->sc_ls_ctl_p_last); } else { UHCI_REMOVE_QH(xfer->qh_start, sc->sc_hs_ctl_p_last); } } if(xfer->pipe->methods == &uhci_device_intr_methods) { UHCI_REMOVE_QH(xfer->qh_start, sc->sc_intr_p_last[xfer->qh_pos]); } /* finish isochronous transfers or clear active bit * (will update xfer->actlen and xfer->frlengths; * should only be called once) */ if(xfer->td_transfer_first && xfer->td_transfer_last) { if(xfer->pipe->methods == &uhci_device_isoc_methods) { if(uhci_isoc_done(sc, xfer)) { need_delay = 1; } } if(need_delay) { td = xfer->td_transfer_first; while(1) { if(td == NULL) { panic("%s:%d: out of TD's\n", __FUNCTION__, __LINE__); } td->td_status &= htole32(~(UHCI_TD_ACTIVE|UHCI_TD_IOC)); if(td == xfer->td_transfer_last) { break; } td = td->obj_next; } } xfer->td_transfer_first = NULL; xfer->td_transfer_last = NULL; } /* stop timeout */ __callout_stop(&xfer->timeout_handle); /* remove interrupt info */ uhci_remove_interrupt_info(xfer); /* wait until hardware has finished any possible * use of the transfer and QH * * hardware finishes in 1 millisecond */ DELAY(need_delay ? ((3*1000)/2) : UHCI_QH_REMOVE_DELAY); if(error) { /* next transfer needs to clear stall */ xfer->pipe->clearstall = 1; } /* transfer is transferred ! */ usbd_transfer_done(xfer,error); /* dequeue transfer (and start next transfer) * * if two transfers are queued, the second * transfer must be started before the * first is called back! */ usbd_transfer_dequeue(xfer); return; } /*---------------------------------------------------------------------------* * uhci bulk support *---------------------------------------------------------------------------*/ static void uhci_device_bulk_open(struct usbd_xfer *xfer) { return; } static void uhci_device_bulk_close(struct usbd_xfer *xfer) { uhci_device_done(xfer, USBD_CANCELLED); return; } static void uhci_device_bulk_enter(struct usbd_xfer *xfer) { /* enqueue transfer */ usbd_transfer_enqueue(xfer); return; } static void uhci_device_bulk_start(struct usbd_xfer *xfer) { uhci_softc_t *sc = xfer->usb_sc; uhci_td_t *td; uhci_qh_t *qh; DPRINTFN(3, ("xfer=%p len=%d\n", xfer, xfer->length)); /* setup TD's */ td = uhci_setup_standard_chain(xfer); /* setup QH */ qh = xfer->qh_start; qh->e_next = td; qh->qh_e_next = td->td_self; UHCI_APPEND_QH(qh, sc->sc_bulk_p_last); uhci_add_loop(sc); xfer->flags |= USBD_BANDWIDTH_RECLAIMED; /**/ uhci_add_interrupt_info(sc, xfer); if(xfer->timeout && (!(xfer->flags & USBD_USE_POLLING))) { __callout_reset(&xfer->timeout_handle, MS_TO_TICKS(xfer->timeout), (void *)(void *)uhci_timeout, xfer); } return; } struct usbd_pipe_methods uhci_device_bulk_methods = { .open = uhci_device_bulk_open, .close = uhci_device_bulk_close, .enter = uhci_device_bulk_enter, .start = uhci_device_bulk_start, .copy_in = usbd_std_bulk_intr_copy_in, .copy_out = usbd_std_bulk_intr_copy_out, }; /*---------------------------------------------------------------------------* * uhci control support *---------------------------------------------------------------------------*/ static void uhci_device_ctrl_open(struct usbd_xfer *xfer) { return; } static void uhci_device_ctrl_close(struct usbd_xfer *xfer) { uhci_device_done(xfer, USBD_CANCELLED); return; } static void uhci_device_ctrl_enter(struct usbd_xfer *xfer) { /* enqueue transfer */ usbd_transfer_enqueue(xfer); return; } static void uhci_device_ctrl_start(struct usbd_xfer *xfer) { uhci_softc_t *sc = xfer->usb_sc; uhci_qh_t *qh; uhci_td_t *td; /* setup TD's */ td = uhci_setup_standard_chain(xfer); /* setup QH */ qh = xfer->qh_start; qh->e_next = td; qh->qh_e_next = td->td_self; /* NOTE: some devices choke on bandwidth- * reclamation for control transfers */ if(xfer->udev->speed == USB_SPEED_LOW) { UHCI_APPEND_QH(qh, sc->sc_ls_ctl_p_last); } else { UHCI_APPEND_QH(qh, sc->sc_hs_ctl_p_last); } /**/ uhci_add_interrupt_info(sc, xfer); if(xfer->timeout && (!(xfer->flags & USBD_USE_POLLING))) { __callout_reset(&xfer->timeout_handle, MS_TO_TICKS(xfer->timeout), (void *)(void *)uhci_timeout, xfer); } return; } struct usbd_pipe_methods uhci_device_ctrl_methods = { .open = uhci_device_ctrl_open, .close = uhci_device_ctrl_close, .enter = uhci_device_ctrl_enter, .start = uhci_device_ctrl_start, .copy_in = usbd_std_ctrl_copy_in, .copy_out = usbd_std_ctrl_copy_out, }; /*---------------------------------------------------------------------------* * uhci interrupt support *---------------------------------------------------------------------------*/ static void uhci_device_intr_open(struct usbd_xfer *xfer) { uhci_softc_t *sc = xfer->usb_sc; u_int16_t best; u_int16_t bit; u_int16_t x; best = 0; bit = UHCI_IFRAMELIST_COUNT/2; while(bit) { if(xfer->interval >= bit) { x = bit; best = bit; while(x & bit) { if(sc->sc_intr_stat[x] < sc->sc_intr_stat[best]) { best = x; } x++; } break; } bit >>= 1; } sc->sc_intr_stat[best]++; xfer->qh_pos = best; DPRINTFN(2, ("best=%d interval=%d\n", best, xfer->interval)); return; } static void uhci_device_intr_close(struct usbd_xfer *xfer) { uhci_softc_t *sc = xfer->usb_sc; sc->sc_intr_stat[xfer->qh_pos]--; uhci_device_done(xfer,USBD_CANCELLED); return; } static void uhci_device_intr_enter(struct usbd_xfer *xfer) { /* enqueue transfer */ usbd_transfer_enqueue(xfer); return; } static void uhci_device_intr_start(struct usbd_xfer *xfer) { uhci_softc_t *sc = xfer->usb_sc; uhci_qh_t *qh; uhci_td_t *td; DPRINTFN(3,("xfer=%p len=%d\n", xfer, xfer->length)); /* setup TD's */ td = uhci_setup_standard_chain(xfer); /* setup QH */ qh = xfer->qh_start; qh->e_next = td; qh->qh_e_next = td->td_self; /* enter QHs into the controller data structures */ UHCI_APPEND_QH(qh, sc->sc_intr_p_last[xfer->qh_pos]); /**/ uhci_add_interrupt_info(sc, xfer); if(xfer->timeout && (!(xfer->flags & USBD_USE_POLLING))) { __callout_reset(&xfer->timeout_handle, MS_TO_TICKS(xfer->timeout), (void *)(void *)uhci_timeout, xfer); } return; } struct usbd_pipe_methods uhci_device_intr_methods = { .open = uhci_device_intr_open, .close = uhci_device_intr_close, .enter = uhci_device_intr_enter, .start = uhci_device_intr_start, .copy_in = usbd_std_bulk_intr_copy_in, .copy_out = usbd_std_bulk_intr_copy_out, }; /*---------------------------------------------------------------------------* * uhci isochronous support *---------------------------------------------------------------------------*/ static void uhci_device_isoc_open(struct usbd_xfer *xfer) { uhci_td_t *td; u_int32_t td_token; td_token = (UE_GET_DIR(xfer->endpoint) == UE_DIR_IN) ? UHCI_TD_IN (0, xfer->endpoint, xfer->address, 0) : UHCI_TD_OUT(0, xfer->endpoint, xfer->address, 0) ; td_token = htole32(td_token); /* initialize all TD's */ for(td = xfer->td_start; td; td = td->obj_next) { /* mark TD as inactive */ td->td_status = htole32(UHCI_TD_IOS); td->td_token = td_token; } return; } static void uhci_device_isoc_close(struct usbd_xfer *xfer) { uhci_device_done(xfer, USBD_CANCELLED); return; } static void uhci_device_isoc_enter(struct usbd_xfer *xfer) { struct usbd_page_search buf_res; struct usbd_page_search fix_res; struct usbd_page_search tmp_res; uhci_softc_t *sc = xfer->usb_sc; u_int32_t buf_offset; u_int32_t fix_offset; u_int32_t nframes; u_int16_t *plen; #ifdef USB_DEBUG u_int8_t once = 1; #endif u_int8_t isread = (UE_GET_DIR(xfer->endpoint) == UE_DIR_IN); uhci_td_t *td; uhci_td_t *td_last = NULL; uhci_td_t **pp_last; DPRINTFN(5,("xfer=%p next=%d nframes=%d\n", xfer, xfer->pipe->isoc_next, xfer->nframes)); nframes = UREAD2(sc, UHCI_FRNUM); if(((nframes - xfer->pipe->isoc_next) & (UHCI_VFRAMELIST_COUNT-1)) < xfer->nframes) { /* not in use yet, schedule it a few frames ahead */ /* data underflow */ xfer->pipe->isoc_next = (nframes + 3) & (UHCI_VFRAMELIST_COUNT-1); DPRINTFN(2,("start next=%d\n", xfer->pipe->isoc_next)); } nframes = xfer->nframes; if(nframes == 0) { /* transfer transferred */ uhci_device_done(xfer, USBD_NORMAL_COMPLETION); /* call callback recursively */ __usbd_callback(xfer); return; } buf_offset = 0; usbd_get_page(&(xfer->buf_data), buf_offset, &buf_res); fix_offset = 0; usbd_get_page(&(xfer->buf_fixup), fix_offset, &fix_res); plen = xfer->frlengths; td = (xfer->td_transfer_first = xfer->td_start); pp_last = &sc->sc_isoc_p_last[xfer->pipe->isoc_next]; /* store starting position */ xfer->qh_pos = xfer->pipe->isoc_next; while(nframes--) { if(td == NULL) { panic("%s:%d: out of TD's\n", __FUNCTION__, __LINE__); } if(pp_last >= &sc->sc_isoc_p_last[UHCI_VFRAMELIST_COUNT]) { pp_last = &sc->sc_isoc_p_last[0]; } if(*plen > xfer->max_frame_size) { #ifdef USB_DEBUG if(once) { once = 0; printf("%s: frame length(%d) exceeds %d " "bytes (frame truncated)\n", __FUNCTION__, *plen, xfer->max_frame_size); } #endif *plen = xfer->max_frame_size; } /* reuse td_token from last transfer */ td->td_token &= htole32(~UHCI_TD_MAXLEN_MASK); td->td_token |= htole32(UHCI_TD_SET_MAXLEN(*plen)); if (buf_res.length < *plen) { /* need to do a fixup */ td->td_buffer = htole32(fix_res.physaddr); if (!isread) { td->fixup_ptr = NULL; usbd_get_page(&(xfer->buf_data), buf_offset + buf_res.length, &tmp_res); /* copy data to fixup location */ bcopy(buf_res.buffer, fix_res.buffer, buf_res.length); bcopy(tmp_res.buffer, ADD_BYTES(fix_res.buffer, buf_res.length), *plen - buf_res.length); } else { td->fixup_ptr = fix_res.buffer; td->fixup_offset = buf_offset; } /* prepare next fixup */ fix_offset += xfer->max_packet_size; usbd_get_page(&(xfer->buf_fixup), fix_offset, &fix_res); } else { td->td_buffer = htole32(buf_res.physaddr); td->fixup_ptr = NULL; } /* update status */ if(nframes == 0) { td->td_status = htole32 (UHCI_TD_ZERO_ACTLEN (UHCI_TD_SET_ERRCNT(0)| UHCI_TD_ACTIVE| UHCI_TD_IOS| UHCI_TD_IOC)); } else { td->td_status = htole32 (UHCI_TD_ZERO_ACTLEN (UHCI_TD_SET_ERRCNT(0)| UHCI_TD_ACTIVE| UHCI_TD_IOS)); } #ifdef USB_DEBUG if(uhcidebug > 5) { DPRINTFN(5,("TD %d\n", nframes)); uhci_dump_td(td); } #endif /* insert TD into schedule */ UHCI_APPEND_TD(td, *pp_last); pp_last++; buf_offset += *plen; usbd_get_page(&(xfer->buf_data), buf_offset, &buf_res); plen++; td_last = td; td = td->obj_next; } xfer->td_transfer_last = td_last; /* update isoc_next */ xfer->pipe->isoc_next = (pp_last - &sc->sc_isoc_p_last[0]) & (UHCI_VFRAMELIST_COUNT-1); /**/ uhci_add_interrupt_info(sc, xfer); if(xfer->timeout && (!(xfer->flags & USBD_USE_POLLING))) { __callout_reset(&xfer->timeout_handle, MS_TO_TICKS(xfer->timeout), (void *)(void *)uhci_timeout, xfer); } /* enqueue transfer * (so that it can be aborted through pipe abort) */ usbd_transfer_enqueue(xfer); return; } static void uhci_device_isoc_start(struct usbd_xfer *xfer) { /* already started, nothing to do */ return; } struct usbd_pipe_methods uhci_device_isoc_methods = { .open = uhci_device_isoc_open, .close = uhci_device_isoc_close, .enter = uhci_device_isoc_enter, .start = uhci_device_isoc_start, .copy_in = usbd_std_isoc_copy_in, .copy_out = usbd_std_isoc_copy_out, }; /*---------------------------------------------------------------------------* * uhci root control support *---------------------------------------------------------------------------* * simulate a hardware hub by handling * all the necessary requests *---------------------------------------------------------------------------*/ static void uhci_root_ctrl_open(struct usbd_xfer *xfer) { return; } static void uhci_root_ctrl_close(struct usbd_xfer *xfer) { uhci_device_done(xfer,USBD_CANCELLED); return; } /* data structures and routines * to emulate the root hub: */ static const usb_device_descriptor_t uhci_devd = { sizeof(usb_device_descriptor_t), UDESC_DEVICE, /* type */ {0x00, 0x01}, /* USB version */ UDCLASS_HUB, /* class */ UDSUBCLASS_HUB, /* subclass */ UDPROTO_FSHUB, /* protocol */ 64, /* max packet */ {0},{0},{0x00,0x01}, /* device id */ 1,2,0, /* string indicies */ 1 /* # of configurations */ }; static const usb_config_descriptor_t uhci_confd = { sizeof(usb_config_descriptor_t), UDESC_CONFIG, {USB_CONFIG_DESCRIPTOR_SIZE + USB_INTERFACE_DESCRIPTOR_SIZE + USB_ENDPOINT_DESCRIPTOR_SIZE}, 1, 1, 0, UC_SELF_POWERED, 0 /* max power */ }; static const usb_interface_descriptor_t uhci_ifcd = { sizeof(usb_interface_descriptor_t), UDESC_INTERFACE, 0, 0, 1, UICLASS_HUB, UISUBCLASS_HUB, UIPROTO_FSHUB, 0 }; static const usb_endpoint_descriptor_t uhci_endpd = { sizeof(usb_endpoint_descriptor_t), UDESC_ENDPOINT, UE_DIR_IN | UHCI_INTR_ENDPT, UE_INTERRUPT, {8}, 255 }; static const usb_hub_descriptor_t uhci_hubd_piix = { USB_HUB_DESCRIPTOR_SIZE, UDESC_HUB, 2, { UHD_PWR_NO_SWITCH | UHD_OC_INDIVIDUAL, 0 }, 50, /* power on to power good */ 0, { 0x00 }, /* both ports are removable */ }; /* * The USB hub protocol requires that SET_FEATURE(PORT_RESET) also * enables the port, and also states that SET_FEATURE(PORT_ENABLE) * should not be used by the USB subsystem. As we cannot issue a * SET_FEATURE(PORT_ENABLE) externally, we must ensure that the port * will be enabled as part of the reset. * * On the VT83C572, the port cannot be successfully enabled until the * outstanding "port enable change" and "connection status change" * events have been reset. */ static usbd_status uhci_portreset(uhci_softc_t *sc, int index) { int lim, port, x; if(index == 1) port = UHCI_PORTSC1; else if(index == 2) port = UHCI_PORTSC2; else return (USBD_IOERROR); x = URWMASK(UREAD2(sc, port)); UWRITE2(sc, port, x | UHCI_PORTSC_PR); DELAY(1000*USB_PORT_ROOT_RESET_DELAY); DPRINTFN(3,("uhci port %d reset, status0 = 0x%04x\n", index, UREAD2(sc, port))); x = URWMASK(UREAD2(sc, port)); UWRITE2(sc, port, x & ~UHCI_PORTSC_PR); DELAY(100); DPRINTFN(3,("uhci port %d reset, status1 = 0x%04x\n", index, UREAD2(sc, port))); x = URWMASK(UREAD2(sc, port)); UWRITE2(sc, port, x | UHCI_PORTSC_PE); lim = 10; while(lim--) { DELAY(1000*USB_PORT_RESET_DELAY); x = UREAD2(sc, port); DPRINTFN(3,("uhci port %d iteration %u, status = 0x%04x\n", index, lim, x)); if(!(x & UHCI_PORTSC_CCS)) { /* * No device is connected (or was disconnected * during reset). Consider the port reset. * The delay must be long enough to ensure on * the initial iteration that the device * connection will have been registered. 50ms * appears to be sufficient, but 20ms is not. */ DPRINTFN(3,("uhci port %d loop %u, device detached\n", index, lim)); goto done; } if(x & (UHCI_PORTSC_POEDC | UHCI_PORTSC_CSC)) { /* * Port enabled changed and/or connection * status changed were set. Reset either or * both raised flags (by writing a 1 to that * bit), and wait again for state to settle. */ UWRITE2(sc, port, URWMASK(x) | (x & (UHCI_PORTSC_POEDC | UHCI_PORTSC_CSC))); continue; } if(x & UHCI_PORTSC_PE) { /* port is enabled */ goto done; } UWRITE2(sc, port, URWMASK(x) | UHCI_PORTSC_PE); } DPRINTFN(1,("uhci port %d reset timed out\n", index)); return (USBD_TIMEOUT); done: DPRINTFN(3,("uhci port %d reset, status2 = 0x%04x\n", index, UREAD2(sc, port))); sc->sc_isreset = 1; return (USBD_NORMAL_COMPLETION); } static void uhci_root_ctrl_enter(struct usbd_xfer *xfer) { uhci_softc_t *sc = xfer->usb_sc; u_int32_t port; u_int32_t x; u_int16_t len; u_int16_t value; u_int16_t index; u_int16_t status; u_int16_t change; u_int16_t l; u_int16_t totlen = 0; union { usb_status_t stat; usb_port_status_t ps; usb_device_request_t req; usb_device_descriptor_t devd; u_int8_t str_temp[128]; u_int8_t byte_temp; } u; usbd_status err; mtx_assert(&sc->sc_bus.mtx, MA_OWNED); if (xfer->length < sizeof(u.req)) { err = USBD_INVAL; goto done; } /* set default actual length */ xfer->actlen = sizeof(u.req); /* copy out "request" */ usbd_copy_out(&(xfer->buf_data), 0, &u.req, sizeof(u.req)); len = (xfer->length - sizeof(u.req)); if (len != UGETW(u.req.wLength)) { err = USBD_INVAL; goto done; } value = UGETW(u.req.wValue); index = UGETW(u.req.wIndex); DPRINTFN(2,("type=0x%02x request=0x%02x wLen=0x%04x " "wValue=0x%04x wIndex=0x%04x\n", u.req.bmRequestType, u.req.bRequest, len, value, index)); #define C(x,y) ((x) | ((y) << 8)) switch(C(u.req.bRequest, u.req.bmRequestType)) { case C(UR_CLEAR_FEATURE, UT_WRITE_DEVICE): case C(UR_CLEAR_FEATURE, UT_WRITE_INTERFACE): case C(UR_CLEAR_FEATURE, UT_WRITE_ENDPOINT): /* * DEVICE_REMOTE_WAKEUP and ENDPOINT_HALT are no-ops * for the integrated root hub. */ break; case C(UR_GET_CONFIG, UT_READ_DEVICE): if(len > 0) { u.byte_temp = sc->sc_conf; totlen = 1; usbd_copy_in(&(xfer->buf_data), sizeof(u.req), &u, totlen); } break; case C(UR_GET_DESCRIPTOR, UT_READ_DEVICE): switch(value >> 8) { case UDESC_DEVICE: if((value & 0xff) != 0) { err = USBD_IOERROR; goto done; } totlen = min(len, sizeof(u.devd)); u.devd = uhci_devd; #if 0 USETW(u.devd.idVendor, sc->sc_id_vendor); #endif usbd_copy_in(&(xfer->buf_data), sizeof(u.req), &u, totlen); break; case UDESC_CONFIG: if((value & 0xff) != 0) { err = USBD_IOERROR; goto done; } totlen = l = min(len, sizeof(uhci_confd)); len -= l; usbd_copy_in(&(xfer->buf_data), sizeof(u.req), &uhci_confd, l); l = min(len, sizeof(uhci_ifcd)); totlen += l; len -= l; usbd_copy_in(&(xfer->buf_data), sizeof(u.req) + sizeof(uhci_confd), &uhci_ifcd, l); l = min(len, sizeof(uhci_endpd)); totlen += l; len -= l; usbd_copy_in(&(xfer->buf_data), sizeof(u.req) + sizeof(uhci_confd) + sizeof(uhci_ifcd), &uhci_endpd, l); break; case UDESC_STRING: if(len == 0) { break; } switch (value & 0xff) { case 0: /* Language table */ totlen = usbd_make_str_desc (u.str_temp, sizeof(u.str_temp), "\001"); break; case 1: /* Vendor */ totlen = usbd_make_str_desc (u.str_temp, sizeof(u.str_temp), sc->sc_vendor); break; case 2: /* Product */ totlen = usbd_make_str_desc (u.str_temp, sizeof(u.str_temp), "UHCI root hub"); break; default: totlen = usbd_make_str_desc (u.str_temp, sizeof(u.str_temp), ""); break; } if (totlen > len) { totlen = len; } usbd_copy_in(&(xfer->buf_data), sizeof(u.req), &u, totlen); break; default: err = USBD_IOERROR; goto done; } break; case C(UR_GET_INTERFACE, UT_READ_INTERFACE): if(len > 0) { u.byte_temp = 0; totlen = 1; usbd_copy_in(&(xfer->buf_data), sizeof(u.req), &u, totlen); } break; case C(UR_GET_STATUS, UT_READ_DEVICE): if(len > 1) { USETW(u.stat.wStatus,UDS_SELF_POWERED); totlen = 2; usbd_copy_in(&(xfer->buf_data), sizeof(u.req), &u, totlen); } break; case C(UR_GET_STATUS, UT_READ_INTERFACE): case C(UR_GET_STATUS, UT_READ_ENDPOINT): if(len > 1) { USETW(u.stat.wStatus, 0); totlen = 2; usbd_copy_in(&(xfer->buf_data), sizeof(u.req), &u, totlen); } break; case C(UR_SET_ADDRESS, UT_WRITE_DEVICE): if(value >= USB_MAX_DEVICES) { err = USBD_IOERROR; goto done; } sc->sc_addr = value; break; case C(UR_SET_CONFIG, UT_WRITE_DEVICE): if((value != 0) && (value != 1)) { err = USBD_IOERROR; goto done; } sc->sc_conf = value; break; case C(UR_SET_DESCRIPTOR, UT_WRITE_DEVICE): break; case C(UR_SET_FEATURE, UT_WRITE_DEVICE): case C(UR_SET_FEATURE, UT_WRITE_INTERFACE): case C(UR_SET_FEATURE, UT_WRITE_ENDPOINT): err = USBD_IOERROR; goto done; case C(UR_SET_INTERFACE, UT_WRITE_INTERFACE): break; case C(UR_SYNCH_FRAME, UT_WRITE_ENDPOINT): break; /* Hub requests */ case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_DEVICE): break; case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_OTHER): DPRINTFN(3, ("UR_CLEAR_PORT_FEATURE " "port=%d feature=%d\n", index, value)); if(index == 1) port = UHCI_PORTSC1; else if(index == 2) port = UHCI_PORTSC2; else { err = USBD_IOERROR; goto done; } switch(value) { case UHF_PORT_ENABLE: x = URWMASK(UREAD2(sc, port)); UWRITE2(sc, port, x & ~UHCI_PORTSC_PE); break; case UHF_PORT_SUSPEND: x = URWMASK(UREAD2(sc, port)); UWRITE2(sc, port, x & ~UHCI_PORTSC_SUSP); break; case UHF_PORT_RESET: x = URWMASK(UREAD2(sc, port)); UWRITE2(sc, port, x & ~UHCI_PORTSC_PR); break; case UHF_C_PORT_CONNECTION: x = URWMASK(UREAD2(sc, port)); UWRITE2(sc, port, x | UHCI_PORTSC_CSC); break; case UHF_C_PORT_ENABLE: x = URWMASK(UREAD2(sc, port)); UWRITE2(sc, port, x | UHCI_PORTSC_POEDC); break; case UHF_C_PORT_OVER_CURRENT: x = URWMASK(UREAD2(sc, port)); UWRITE2(sc, port, x | UHCI_PORTSC_OCIC); break; case UHF_C_PORT_RESET: sc->sc_isreset = 0; err = USBD_NORMAL_COMPLETION; goto done; case UHF_PORT_CONNECTION: case UHF_PORT_OVER_CURRENT: case UHF_PORT_POWER: case UHF_PORT_LOW_SPEED: case UHF_C_PORT_SUSPEND: default: err = USBD_IOERROR; goto done; } break; case C(UR_GET_BUS_STATE, UT_READ_CLASS_OTHER): if(index == 1) port = UHCI_PORTSC1; else if(index == 2) port = UHCI_PORTSC2; else { err = USBD_IOERROR; goto done; } if(len > 0) { u.byte_temp = ((UREAD2(sc, port) & UHCI_PORTSC_LS) >> UHCI_PORTSC_LS_SHIFT); totlen = 1; usbd_copy_in(&(xfer->buf_data), sizeof(u.req), &u, totlen); } break; case C(UR_GET_DESCRIPTOR, UT_READ_CLASS_DEVICE): if((value & 0xff) != 0) { err = USBD_IOERROR; goto done; } totlen = min(len, USB_HUB_DESCRIPTOR_SIZE); usbd_copy_in(&(xfer->buf_data), sizeof(u.req), &uhci_hubd_piix, totlen); break; case C(UR_GET_STATUS, UT_READ_CLASS_DEVICE): if(len < 4) { err = USBD_IOERROR; goto done; } usbd_bzero(&(xfer->buf_data), sizeof(u.req), len); totlen = len; break; case C(UR_GET_STATUS, UT_READ_CLASS_OTHER): if(index == 1) port = UHCI_PORTSC1; else if(index == 2) port = UHCI_PORTSC2; else { err = USBD_IOERROR; goto done; } if(len < 4) { err = USBD_IOERROR; goto done; } x = UREAD2(sc, port); status = change = 0; if(x & UHCI_PORTSC_CCS) status |= UPS_CURRENT_CONNECT_STATUS; if(x & UHCI_PORTSC_CSC) change |= UPS_C_CONNECT_STATUS; if(x & UHCI_PORTSC_PE) status |= UPS_PORT_ENABLED; if(x & UHCI_PORTSC_POEDC) change |= UPS_C_PORT_ENABLED; if(x & UHCI_PORTSC_OCI) status |= UPS_OVERCURRENT_INDICATOR; if(x & UHCI_PORTSC_OCIC) change |= UPS_C_OVERCURRENT_INDICATOR; if(x & UHCI_PORTSC_SUSP) status |= UPS_SUSPEND; if(x & UHCI_PORTSC_LSDA) status |= UPS_LOW_SPEED; status |= UPS_PORT_POWER; if(sc->sc_isreset) change |= UPS_C_PORT_RESET; USETW(u.ps.wPortStatus, status); USETW(u.ps.wPortChange, change); totlen = min(len, sizeof(u.ps)); usbd_copy_in(&(xfer->buf_data), sizeof(u.req), &u, totlen); break; case C(UR_SET_DESCRIPTOR, UT_WRITE_CLASS_DEVICE): err = USBD_IOERROR; goto done; case C(UR_SET_FEATURE, UT_WRITE_CLASS_DEVICE): break; case C(UR_SET_FEATURE, UT_WRITE_CLASS_OTHER): if(index == 1) port = UHCI_PORTSC1; else if(index == 2) port = UHCI_PORTSC2; else { err = USBD_IOERROR; goto done; } switch(value) { case UHF_PORT_ENABLE: x = URWMASK(UREAD2(sc, port)); UWRITE2(sc, port, x | UHCI_PORTSC_PE); break; case UHF_PORT_SUSPEND: x = URWMASK(UREAD2(sc, port)); UWRITE2(sc, port, x | UHCI_PORTSC_SUSP); break; case UHF_PORT_RESET: err = uhci_portreset(sc, index); goto done; case UHF_PORT_POWER: /* pretend we turned on power */ err = USBD_NORMAL_COMPLETION; goto done; case UHF_C_PORT_CONNECTION: case UHF_C_PORT_ENABLE: case UHF_C_PORT_OVER_CURRENT: case UHF_PORT_CONNECTION: case UHF_PORT_OVER_CURRENT: case UHF_PORT_LOW_SPEED: case UHF_C_PORT_SUSPEND: case UHF_C_PORT_RESET: default: err = USBD_IOERROR; goto done; } break; default: err = USBD_IOERROR; goto done; } xfer->actlen = totlen + sizeof(u.req); err = USBD_NORMAL_COMPLETION; done: /* transfer transferred */ uhci_device_done(xfer,err); /* call callback recursively */ __usbd_callback(xfer); return; } static void uhci_root_ctrl_start(struct usbd_xfer *xfer) { /* not used */ return; } struct usbd_pipe_methods uhci_root_ctrl_methods = { .open = uhci_root_ctrl_open, .close = uhci_root_ctrl_close, .enter = uhci_root_ctrl_enter, .start = uhci_root_ctrl_start, .copy_in = usbd_std_ctrl_copy_in, .copy_out = usbd_std_ctrl_copy_out, }; /*---------------------------------------------------------------------------* * uhci root interrupt support *---------------------------------------------------------------------------*/ static void uhci_root_intr_open(struct usbd_xfer *xfer) { return; } static void uhci_root_intr_close(struct usbd_xfer *xfer) { uhci_device_done(xfer, USBD_CANCELLED); return; } static void uhci_root_intr_enter(struct usbd_xfer *xfer) { /* enqueue transfer * (so that it can be aborted through pipe abort) */ usbd_transfer_enqueue(xfer); return; } static void uhci_root_intr_check(struct usbd_xfer *xfer); static void uhci_root_intr_start(struct usbd_xfer *xfer) { DPRINTFN(3, ("xfer=%p len=%d\n", xfer, xfer->length)); __callout_reset(&xfer->timeout_handle, MS_TO_TICKS(xfer->interval), (void *)(void *)uhci_root_intr_check, xfer); return; } /* * this routine is executed periodically and simulates interrupts * from the root controller interrupt pipe for port status change */ static void uhci_root_intr_check(struct usbd_xfer *xfer) { struct usbd_callback_info info[1]; uhci_softc_t *sc = xfer->usb_sc; u_int8_t buf[1]; DPRINTFN(20,("\n")); mtx_assert(&sc->sc_bus.mtx, MA_OWNED); buf[0] = 0; if(UREAD2(sc, UHCI_PORTSC1) & (UHCI_PORTSC_CSC|UHCI_PORTSC_OCIC)) { buf[0] |= 1<<1; } if(UREAD2(sc, UHCI_PORTSC2) & (UHCI_PORTSC_CSC|UHCI_PORTSC_OCIC)) { buf[0] |= 1<<2; } if((buf[0] == 0) || !(UREAD2(sc, UHCI_CMD) & UHCI_CMD_RS)) { /* no change or controller not running, * try again in a while */ uhci_root_intr_start(xfer); mtx_unlock(&sc->sc_bus.mtx); } else { if (xfer->length) { xfer->actlen = 1; usbd_copy_in(&(xfer->buf_data), 0, buf, 1); } else { xfer->actlen = 0; } /* transfer is transferred */ uhci_device_done(xfer, USBD_NORMAL_COMPLETION); /* queue callback */ info[0].xfer = xfer; info[0].refcount = xfer->usb_refcount; xfer->usb_root->memory_refcount++; mtx_unlock(&sc->sc_bus.mtx); usbd_do_callback(&info[0],&info[1]); } return; } struct usbd_pipe_methods uhci_root_intr_methods = { .open = uhci_root_intr_open, .close = uhci_root_intr_close, .enter = uhci_root_intr_enter, .start = uhci_root_intr_start, .copy_in = usbd_std_bulk_intr_copy_in, .copy_out = usbd_std_bulk_intr_copy_out, }; static usbd_status uhci_xfer_setup(struct usbd_device *udev, u_int8_t iface_index, struct usbd_xfer **pxfer, const struct usbd_config *setup_start, const struct usbd_config *setup_end) { uhci_softc_t *sc = UHCI_BUS2SC(udev->bus); const struct usbd_config *setup; struct usbd_memory_info *info; struct usbd_page *page_ptr; struct usbd_xfer dummy; struct usbd_xfer *xfer; u_int32_t size[2]; u_int32_t total_size[2]; u_int32_t ntd; u_int32_t nqh; u_int32_t nfixup; u_int32_t n; void *buf; void *temp_ptr; void *last_obj; bus_size_t temp_phy; usbd_status error = 0; buf = NULL; page_ptr = NULL; total_size[0] = 0; total_size[1] = 0; repeat: size[0] = 0; size[1] = 0; /* align data to 8 byte boundary */ size[0] += ((-size[0]) & (USB_HOST_ALIGN-1)); if(buf) { info = ADD_BYTES(buf,size[0]); info->memory_base = buf; info->memory_size = total_size[0]; info->page_base = page_ptr; info->page_size = total_size[1]; info->usb_mtx = &sc->sc_bus.mtx; } else { info = NULL; } size[0] += sizeof(info[0]); for(setup = setup_start; setup < setup_end; setup++) { ntd = 0; nqh = 0; /* align data to 8 byte boundary */ size[0] += ((-size[0]) & (USB_HOST_ALIGN-1)); if(buf) { xfer = ADD_BYTES(buf,size[0]); *pxfer++ = xfer; } else { /* need dummy xfer to * calculate ntd and nqh ! */ xfer = &dummy; bzero(&dummy, sizeof(dummy)); } /* * setup xfer */ xfer->usb_sc = sc; xfer->usb_mtx = &sc->sc_bus.mtx; xfer->usb_root = info; xfer->address = udev->address; xfer->pipe = usbd_get_pipe(udev, iface_index, setup); if(!xfer->pipe) { error = USBD_NO_PIPE; DPRINTF(("no pipe for endpoint %d\n", setup->endpoint)); goto done; } else { usbd_std_transfer_setup(xfer, setup, 0x500, 0x500); /* * compute ntd and nqh */ if((xfer->pipe->methods == &uhci_device_ctrl_methods) || (xfer->pipe->methods == &uhci_device_bulk_methods) || (xfer->pipe->methods == &uhci_device_intr_methods)) { nqh = 1; ntd = (1+ /* SETUP */ 1+ /* STATUS */ 1 /* SHORTPKT */) + (xfer->length / xfer->max_packet_size) /* DATA */; } if(xfer->pipe->methods == &uhci_device_isoc_methods) { if(xfer->nframes >= UHCI_VFRAMELIST_COUNT) { error = USBD_INVAL; DPRINTF(("isochronous frame-limit " "exceeded by 0x%x frames; " "endpoint 0x%02x\n", setup->frames - UHCI_VFRAMELIST_COUNT, setup->endpoint)); goto done; } if(xfer->nframes == 0) { error = USBD_ZERO_FRAMES_IN_ISOC_MODE; DPRINTF(("frames == 0 in isochronous mode; " "endpoint 0x%02x\n", setup->endpoint)); goto done; } ntd = xfer->nframes; } } size[0] += sizeof(xfer[0]); if(xfer->nframes) { /* align data to 8 byte boundary */ size[0] += ((-size[0]) & (USB_HOST_ALIGN-1)); xfer->frlengths = ADD_BYTES(buf,size[0]); size[0] += (xfer->nframes * sizeof(xfer->frlengths[0])); xfer->frlengths_old = ADD_BYTES(buf,size[0]); size[0] += (xfer->nframes * sizeof(xfer->frlengths[0])); } if (!(xfer->flags & USBD_USE_DMA)) { /* align data to 8 byte boundary */ size[0] += ((-size[0]) & (USB_HOST_ALIGN-1)); xfer->buffer = ADD_BYTES(buf,size[0]); size[0] += xfer->length; } /*****/ /* align data to 8 byte boundary */ size[1] += ((-size[1]) & (USB_HOST_ALIGN-1)); usbd_page_set_start(&(xfer->buf_data), page_ptr, size[1]); /* * NOTE: the UHCI controller requires that * every packet must be continuous on * the same USB memory page ! */ if (xfer->pipe->methods == &uhci_device_isoc_methods) { size[1] += xfer->length; nfixup = (xfer->length / USB_PAGE_SIZE) + 1; } else { n = xfer->length; nfixup = 0; if (xfer->pipe->methods == &uhci_device_ctrl_methods) { if (n >= sizeof(usb_device_request_t)) { size[1] += usbd_page_fit_obj(page_ptr, size[1], sizeof(usb_device_request_t)); size[1] += sizeof(usb_device_request_t); n -= sizeof(usb_device_request_t); } } while (n >= xfer->max_packet_size) { size[1] += usbd_page_fit_obj(page_ptr, size[1], xfer->max_packet_size); size[1] += xfer->max_packet_size; n -= xfer->max_packet_size; } size[1] += usbd_page_fit_obj(page_ptr, size[1], n); size[1] += n; } usbd_page_set_end(&(xfer->buf_data), page_ptr, size[1]); /* align data to 8 byte boundary */ size[1] += ((-size[1]) & (USB_HOST_ALIGN-1)); usbd_page_set_start(&(xfer->buf_fixup), page_ptr, size[1]); for (n = 0; n < nfixup; n++) { size[1] += usbd_page_fit_obj(page_ptr, size[1], xfer->max_frame_size); size[1] += xfer->max_frame_size; } usbd_page_set_end(&(xfer->buf_fixup), page_ptr, size[1]); size[1] += ((-size[1]) & (UHCI_TD_ALIGN-1)); /* align data */ last_obj = NULL; for(n = 0; n < ntd; n++) { size[1] += usbd_page_fit_obj(page_ptr, size[1], sizeof(uhci_td_t)); if(buf) { temp_ptr = usbd_page_get_buf(page_ptr,size[1]); temp_phy = usbd_page_get_phy(page_ptr,size[1]); /* init TD */ ((uhci_td_t *)temp_ptr)->td_self = htole32(temp_phy|UHCI_PTR_TD); if((xfer->pipe->methods == &uhci_device_bulk_methods) || (xfer->pipe->methods == &uhci_device_ctrl_methods) || (xfer->pipe->methods == &uhci_device_intr_methods)) { /* set depth first bit */ ((uhci_td_t *)temp_ptr)->td_self |= htole32(UHCI_PTR_VF); } ((uhci_td_t *)temp_ptr)->obj_next = last_obj; last_obj = temp_ptr; } size[1] += sizeof(uhci_td_t); } xfer->td_start = last_obj; size[1] += ((-size[1]) & (UHCI_QH_ALIGN-1)); /* align data */ last_obj = NULL; for(n = 0; n < nqh; n++) { size[1] += usbd_page_fit_obj(page_ptr, size[1], sizeof(uhci_qh_t)); if(buf) { temp_ptr = usbd_page_get_buf(page_ptr,size[1]); temp_phy = usbd_page_get_phy(page_ptr,size[1]); /* init QH */ ((uhci_qh_t *)temp_ptr)->qh_self = htole32(temp_phy|UHCI_PTR_QH); ((uhci_qh_t *)temp_ptr)->obj_next = last_obj; last_obj = temp_ptr; } size[1] += sizeof(uhci_qh_t); } xfer->qh_start = last_obj; } if(buf || error) { goto done; } /* compute number of USB pages required */ total_size[1] = (size[1] + USB_PAGE_SIZE - 1) / USB_PAGE_SIZE; /* align data to 8 byte boundary */ size[0] += ((-size[0]) & (USB_HOST_ALIGN-1)); /* store offset temporarily */ n = size[0]; size[0] += (sizeof(*page_ptr) * total_size[1]); /* store total buffer size */ total_size[0] = size[0]; /* allocate zeroed memory */ buf = malloc(total_size[0], M_USB, M_WAITOK|M_ZERO); if(buf == NULL) { error = USBD_NOMEM; DPRINTF(("cannot allocate memory block for " "configuration (%d bytes)\n", total_size[0])); goto done; } page_ptr = ADD_BYTES(buf,n); if (usbd_page_alloc(sc->sc_bus.dma_tag, page_ptr, total_size[1])) { free(buf, M_USB); error = USBD_NOMEM; DPRINTF(("cannot allocate memory block for " "configuration (%d USB pages)\n", total_size[1])); goto done; } goto repeat; done: return error; } static void uhci_pipe_init(struct usbd_device *udev, usb_endpoint_descriptor_t *edesc, struct usbd_pipe *pipe) { uhci_softc_t *sc = UHCI_BUS2SC(udev->bus); DPRINTFN(1, ("pipe=%p, addr=%d, endpt=%d (%d)\n", pipe, udev->address, edesc->bEndpointAddress, sc->sc_addr)); if(udev->address == sc->sc_addr) { switch (edesc->bEndpointAddress) { case USB_CONTROL_ENDPOINT: pipe->methods = &uhci_root_ctrl_methods; break; case UE_DIR_IN | UHCI_INTR_ENDPT: pipe->methods = &uhci_root_intr_methods; break; default: panic("invalid endpoint address: 0x%02x", edesc->bEndpointAddress); break; } } else { switch (edesc->bmAttributes & UE_XFERTYPE) { case UE_CONTROL: pipe->methods = &uhci_device_ctrl_methods; break; case UE_INTERRUPT: pipe->methods = &uhci_device_intr_methods; break; case UE_ISOCHRONOUS: pipe->methods = &uhci_device_isoc_methods; break; case UE_BULK: pipe->methods = &uhci_device_bulk_methods; break; } } return; } struct usbd_bus_methods uhci_bus_methods = { .pipe_init = uhci_pipe_init, .xfer_setup = uhci_xfer_setup, .do_poll = uhci_do_poll, }; pwcbsd/usb/uhci.h000644 000423 000000 00000022646 10551670754 014514 0ustar00luigiwheel000000 000000 /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifndef _UHCI_H_ #define _UHCI_H_ /* PCI config registers */ #define PCI_USBREV 0x60 /* USB protocol revision */ #define PCI_USBREV_MASK 0xff #define PCI_USBREV_PRE_1_0 0x00 #define PCI_USBREV_1_0 0x10 #define PCI_USBREV_1_1 0x11 #define PCI_LEGSUP 0xc0 /* Legacy Support register */ #define PCI_LEGSUP_USBPIRQDEN 0x2000 /* USB PIRQ D Enable */ #define PCI_CBIO 0x20 /* configuration base IO */ #define PCI_INTERFACE_UHCI 0x00 /* UHCI registers */ #define UHCI_CMD 0x00 #define UHCI_CMD_RS 0x0001 #define UHCI_CMD_HCRESET 0x0002 #define UHCI_CMD_GRESET 0x0004 #define UHCI_CMD_EGSM 0x0008 #define UHCI_CMD_FGR 0x0010 #define UHCI_CMD_SWDBG 0x0020 #define UHCI_CMD_CF 0x0040 #define UHCI_CMD_MAXP 0x0080 #define UHCI_STS 0x02 #define UHCI_STS_USBINT 0x0001 #define UHCI_STS_USBEI 0x0002 #define UHCI_STS_RD 0x0004 #define UHCI_STS_HSE 0x0008 #define UHCI_STS_HCPE 0x0010 #define UHCI_STS_HCH 0x0020 #define UHCI_STS_ALLINTRS 0x003f #define UHCI_INTR 0x04 #define UHCI_INTR_TOCRCIE 0x0001 #define UHCI_INTR_RIE 0x0002 #define UHCI_INTR_IOCE 0x0004 #define UHCI_INTR_SPIE 0x0008 #define UHCI_FRNUM 0x06 #define UHCI_FRNUM_MASK 0x03ff #define UHCI_FLBASEADDR 0x08 #define UHCI_SOF 0x0c #define UHCI_SOF_MASK 0x7f #define UHCI_PORTSC1 0x010 #define UHCI_PORTSC2 0x012 #define UHCI_PORTSC_CCS 0x0001 #define UHCI_PORTSC_CSC 0x0002 #define UHCI_PORTSC_PE 0x0004 #define UHCI_PORTSC_POEDC 0x0008 #define UHCI_PORTSC_LS 0x0030 #define UHCI_PORTSC_LS_SHIFT 4 #define UHCI_PORTSC_RD 0x0040 #define UHCI_PORTSC_LSDA 0x0100 #define UHCI_PORTSC_PR 0x0200 #define UHCI_PORTSC_OCI 0x0400 #define UHCI_PORTSC_OCIC 0x0800 #define UHCI_PORTSC_SUSP 0x1000 #define URWMASK(x) ((x) & (UHCI_PORTSC_SUSP | \ UHCI_PORTSC_PR | UHCI_PORTSC_RD | \ UHCI_PORTSC_PE)) #define UHCI_FRAMELIST_COUNT 1024 /* units */ #define UHCI_FRAMELIST_ALIGN 4096 /* bytes */ /* Structures alignment (bytes) */ #define UHCI_TD_ALIGN 16 #define UHCI_QH_ALIGN 16 #if ((USB_PAGE_SIZE < UHCI_TD_ALIGN) || (UHCI_TD_ALIGN == 0) || \ (USB_PAGE_SIZE < UHCI_QH_ALIGN) || (UHCI_QH_ALIGN == 0)) #error "Invalid USB page size!" #endif typedef u_int32_t uhci_physaddr_t; #define UHCI_PTR_T 0x00000001 #define UHCI_PTR_TD 0x00000000 #define UHCI_PTR_QH 0x00000002 #define UHCI_PTR_VF 0x00000004 #define UHCI_QH_REMOVE_DELAY 5 /* us - QH remove delay */ /* * The Queue Heads (QH) and Transfer Descriptors (TD) are accessed by * both the CPU and the USB-controller which run concurrently. Great * care must be taken. When the data-structures are linked into the * USB controller's frame list, the USB-controller "owns" the * td_status and qh_elink fields, which will not be written by the * CPU. * */ typedef struct uhci_td { /* * Data used by the UHCI controller. * volatile is used in order to mantain struct members ordering. */ volatile uint32_t td_next; volatile uint32_t td_status; #define UHCI_TD_GET_ACTLEN(s) (((s) + 1) & 0x3ff) #define UHCI_TD_ZERO_ACTLEN(t) ((t) | 0x3ff) #define UHCI_TD_BITSTUFF 0x00020000 #define UHCI_TD_CRCTO 0x00040000 #define UHCI_TD_NAK 0x00080000 #define UHCI_TD_BABBLE 0x00100000 #define UHCI_TD_DBUFFER 0x00200000 #define UHCI_TD_STALLED 0x00400000 #define UHCI_TD_ACTIVE 0x00800000 #define UHCI_TD_IOC 0x01000000 #define UHCI_TD_IOS 0x02000000 #define UHCI_TD_LS 0x04000000 #define UHCI_TD_GET_ERRCNT(s) (((s) >> 27) & 3) #define UHCI_TD_SET_ERRCNT(n) ((n) << 27) #define UHCI_TD_SPD 0x20000000 volatile uint32_t td_token; #define UHCI_TD_PID 0x000000ff #define UHCI_TD_PID_IN 0x00000069 #define UHCI_TD_PID_OUT 0x000000e1 #define UHCI_TD_PID_SETUP 0x0000002d #define UHCI_TD_GET_PID(s) ((s) & 0xff) #define UHCI_TD_SET_DEVADDR(a) ((a) << 8) #define UHCI_TD_GET_DEVADDR(s) (((s) >> 8) & 0x7f) #define UHCI_TD_SET_ENDPT(e) (((e) & 0xf) << 15) #define UHCI_TD_GET_ENDPT(s) (((s) >> 15) & 0xf) #define UHCI_TD_SET_DT(t) ((t) << 19) #define UHCI_TD_GET_DT(s) (((s) >> 19) & 1) #define UHCI_TD_SET_MAXLEN(l) (((l)-1) << 21) #define UHCI_TD_GET_MAXLEN(s) ((((s) >> 21) + 1) & 0x7ff) #define UHCI_TD_MAXLEN_MASK 0xffe00000 volatile uint32_t td_buffer; /* * Extra information needed: */ struct uhci_td *next; struct uhci_td *prev; struct uhci_td *obj_next; void *fixup_ptr; uint32_t td_self; uint32_t fixup_offset; } __aligned(UHCI_TD_ALIGN) uhci_td_t; #define UHCI_TD_ERROR (UHCI_TD_BITSTUFF | UHCI_TD_CRCTO | \ UHCI_TD_BABBLE | UHCI_TD_DBUFFER | UHCI_TD_STALLED) #define UHCI_TD_SETUP(len, endp, dev) (UHCI_TD_SET_MAXLEN(len) | \ UHCI_TD_SET_ENDPT(endp) | \ UHCI_TD_SET_DEVADDR(dev) | \ UHCI_TD_PID_SETUP) #define UHCI_TD_OUT(len, endp, dev, dt) (UHCI_TD_SET_MAXLEN(len) | \ UHCI_TD_SET_ENDPT(endp) | \ UHCI_TD_SET_DEVADDR(dev) | \ UHCI_TD_PID_OUT | UHCI_TD_SET_DT(dt)) #define UHCI_TD_IN(len, endp, dev, dt) (UHCI_TD_SET_MAXLEN(len) | \ UHCI_TD_SET_ENDPT(endp) | \ UHCI_TD_SET_DEVADDR(dev) | \ UHCI_TD_PID_IN | UHCI_TD_SET_DT(dt)) typedef struct uhci_qh { /* * Data used by the UHCI controller. */ volatile uint32_t qh_h_next; volatile uint32_t qh_e_next; /* * Extra information needed: */ struct uhci_qh *h_next; struct uhci_qh *h_prev; struct uhci_qh *obj_next; struct uhci_td *e_next; uint32_t qh_self; uint16_t intr_pos; } __aligned(UHCI_QH_ALIGN) uhci_qh_t; /* Maximum number of isochronous TD's and QH's interrupt */ #define UHCI_VFRAMELIST_COUNT 128 #define UHCI_IFRAMELIST_COUNT (2 * UHCI_VFRAMELIST_COUNT) #if (((UHCI_VFRAMELIST_COUNT & (UHCI_VFRAMELIST_COUNT-1)) != 0) || \ (UHCI_VFRAMELIST_COUNT > UHCI_FRAMELIST_COUNT)) #error "UHCI_VFRAMELIST_COUNT is not power of two" #error "or UHCI_VFRAMELIST_COUNT > UHCI_FRAMELIST_COUNT" #endif struct uhci_hw_softc { uint32_t pframes[UHCI_FRAMELIST_COUNT]; /* start TD pointer */ struct uhci_td isoc_start[UHCI_VFRAMELIST_COUNT]; /* start TD for isochronous */ struct uhci_qh intr_start[UHCI_IFRAMELIST_COUNT]; /* start QH for interrupt */ struct uhci_qh ls_ctl_start; /* start QH for low speed control */ struct uhci_qh hs_ctl_start; /* start QH for high speed control */ struct uhci_qh bulk_start; /* start QH for bulk */ struct uhci_qh last_qh; /* last QH */ struct uhci_td last_td; /* last TD */ }; typedef struct uhci_softc { struct uhci_hw_softc sc_hw; /* hardware structures first */ uint32_t sc_physaddr; /* physical address of this structure */ struct uhci_td *sc_isoc_p_last[UHCI_VFRAMELIST_COUNT]; /* pointer to last TD for isochronous */ struct uhci_qh *sc_intr_p_last[UHCI_IFRAMELIST_COUNT]; /* pointer to last QH for interrupt */ uint16_t sc_intr_stat[UHCI_IFRAMELIST_COUNT]; struct uhci_qh *sc_ls_ctl_p_last; /* pointer to last QH for low speed control */ struct uhci_qh *sc_hs_ctl_p_last; /* pointer to last QH for high speed control */ struct uhci_qh *sc_bulk_p_last; /* pointer to last QH for bulk */ struct usbd_bus sc_bus; /* base device */ bus_space_tag_t iot; bus_space_handle_t ioh; bus_size_t ios; void *ih; struct resource *io_res; struct resource *irq_res; device_t sc_dev; uint32_t sc_loops; /* number of QHs that wants looping */ uint8_t sc_addr; /* device address */ uint8_t sc_conf; /* device configuration */ uint8_t sc_isreset; uint8_t sc_saved_sof; uint16_t sc_saved_frnum; LIST_HEAD(, usbd_xfer) sc_interrupt_list_head; char sc_vendor[16]; /* vendor string for root hub */ } uhci_softc_t; usbd_status uhci_init(uhci_softc_t *sc); void uhci_suspend(uhci_softc_t *sc); void uhci_resume(uhci_softc_t *sc); void uhci_reset(uhci_softc_t *sc); void uhci_interrupt(uhci_softc_t *sc); #endif /* _UHCI_H_ */ pwcbsd/usb/uhci_pci.c000644 000423 000000 00000025534 10551670754 015341 0ustar00luigiwheel000000 000000 /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (augustss@carlstedt.se) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD: src/sys/dev/usb/uhci_pci.c,v 1.58 2006/05/28 05:27:08 iedowse Exp $"); /* Universal Host Controller Interface * * UHCI spec: http://www.intel.com/ */ /* The low level controller code for UHCI has been split into * PCI probes and UHCI specific code. This was done to facilitate the * sharing of code between *BSD's */ #include "opt_bus.h" #include #include #include #include #include #include /* LIST_XXX() */ #include #include #define INCLUDE_PCIXXX_H #include #include #include #include #define PCI_UHCI_VENDORID_INTEL 0x8086 #define PCI_UHCI_VENDORID_VIA 0x1106 /* PIIX4E has no separate stepping */ #define PCI_UHCI_BASE_REG 0x20 static int uhci_pci_attach(device_t self); static int uhci_pci_detach(device_t self); static int uhci_pci_suspend(device_t self); static int uhci_pci_resume(device_t self); static int uhci_pci_suspend(device_t self) { uhci_softc_t *sc = device_get_softc(self); int err; err = bus_generic_suspend(self); if(err) { return err; } uhci_suspend(sc); return 0; } static int uhci_pci_resume(device_t self) { uhci_softc_t *sc = device_get_softc(self); pci_write_config(self, PCI_LEGSUP, PCI_LEGSUP_USBPIRQDEN, 2); uhci_resume(sc); bus_generic_resume(self); return 0; } static const char * uhci_pci_match(device_t self) { u_int32_t device_id = pci_get_devid(self); if(device_id == 0x70208086) { return ("Intel 82371SB (PIIX3) USB controller"); } if(device_id == 0x71128086) { return ("Intel 82371AB/EB (PIIX4) USB controller"); } if(device_id == 0x24128086) { return ("Intel 82801AA (ICH) USB controller"); } if(device_id == 0x24228086) { return ("Intel 82801AB (ICH0) USB controller"); } if(device_id == 0x24428086) { return ("Intel 82801BA/BAM (ICH2) USB controller USB-A"); } if(device_id == 0x24448086) { return ("Intel 82801BA/BAM (ICH2) USB controller USB-B"); } if(device_id == 0x24828086) { return ("Intel 82801CA/CAM (ICH3) USB controller USB-A"); } if(device_id == 0x24848086) { return ("Intel 82801CA/CAM (ICH3) USB controller USB-B"); } if(device_id == 0x24878086) { return ("Intel 82801CA/CAM (ICH3) USB controller USB-C"); } if(device_id == 0x24c28086) { return ("Intel 82801DB (ICH4) USB controller USB-A"); } if(device_id == 0x24c48086) { return ("Intel 82801DB (ICH4) USB controller USB-B"); } if(device_id == 0x24c78086) { return ("Intel 82801DB (ICH4) USB controller USB-C"); } if(device_id == 0x24d28086) { return ("Intel 82801EB (ICH5) USB controller USB-A"); } if(device_id == 0x24d48086) { return ("Intel 82801EB (ICH5) USB controller USB-B"); } if(device_id == 0x24d78086) { return ("Intel 82801EB (ICH5) USB controller USB-C"); } if(device_id == 0x24de8086) { return ("Intel 82801EB (ICH5) USB controller USB-D"); } if(device_id == 0x26588086) { return ("Intel 82801FB/FR/FW/FRW (ICH6) USB controller USB-A"); } if(device_id == 0x26598086) { return ("Intel 82801FB/FR/FW/FRW (ICH6) USB controller USB-B"); } if(device_id == 0x265a8086) { return ("Intel 82801FB/FR/FW/FRW (ICH6) USB controller USB-C"); } if(device_id == 0x265b8086) { return ("Intel 82801FB/FR/FW/FRW (ICH6) USB controller USB-D"); } if(device_id == 0x719a8086) { return ("Intel 82443MX USB controller"); } if(device_id == 0x76028086) { return ("Intel 82372FB/82468GX USB controller"); } if(device_id == 0x30381106) { return ("VIA 83C572 USB controller"); } if((pci_get_class(self) == PCIC_SERIALBUS) && (pci_get_subclass(self) == PCIS_SERIALBUS_USB) && (pci_get_progif(self) == PCI_INTERFACE_UHCI)) { return ("UHCI (generic) USB controller"); } return NULL; } static int uhci_pci_probe(device_t self) { const char *desc = uhci_pci_match(self); if(desc) { device_set_desc(self, desc); return 0; } else { return ENXIO; } } static int uhci_pci_attach(device_t self) { uhci_softc_t *sc; int rid; int err; sc = usbd_mem_alloc(device_get_dma_tag(self), sizeof(*sc), LOG2(UHCI_FRAMELIST_ALIGN)); if(sc == NULL) { device_printf(self, "Could not allocate sc\n"); return ENXIO; } #if 1 bzero(sc, sizeof(*sc)); #endif sc->sc_physaddr = usbd_mem_vtophys(sc, sizeof(*sc)); /* physical address of sc */ mtx_init(&sc->sc_bus.mtx, "usb lock", NULL, MTX_DEF|MTX_RECURSE); device_set_softc(self, sc); sc->sc_dev = self; pci_enable_busmaster(self); sc->sc_bus.dma_tag = usbd_dma_tag_alloc(device_get_dma_tag(self), USB_PAGE_SIZE, USB_PAGE_SIZE); if (sc->sc_bus.dma_tag == NULL) { device_printf(self, "Could not allocate DMA tag\n"); goto error; } rid = PCI_UHCI_BASE_REG; sc->io_res = bus_alloc_resource_any(self, SYS_RES_IOPORT, &rid, RF_ACTIVE); if(!sc->io_res) { device_printf(self, "Could not map ports\n"); goto error; } sc->iot = rman_get_bustag(sc->io_res); sc->ioh = rman_get_bushandle(sc->io_res); /* disable interrupts */ bus_space_write_2(sc->iot, sc->ioh, UHCI_INTR, 0); rid = 0; sc->irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); if(sc->irq_res == NULL) { device_printf(self, "Could not allocate irq\n"); goto error; } sc->sc_bus.bdev = device_add_child(self, "usb", -1); if(!sc->sc_bus.bdev) { device_printf(self, "Could not add USB device\n"); goto error; } device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); device_set_softc(sc->sc_bus.bdev, &sc->sc_bus); /* uhci_pci_match must never return NULL if uhci_pci_probe succeeded */ device_set_desc(sc->sc_bus.bdev, uhci_pci_match(self)); switch (pci_get_vendor(self)) { case PCI_UHCI_VENDORID_INTEL: sprintf(sc->sc_vendor, "Intel"); break; case PCI_UHCI_VENDORID_VIA: sprintf(sc->sc_vendor, "VIA"); break; default: if(bootverbose) { device_printf(self, "(New UHCI DeviceId=0x%08x)\n", pci_get_devid(self)); } sprintf(sc->sc_vendor, "(0x%04x)", pci_get_vendor(self)); } switch (pci_read_config(self, PCI_USBREV, 1) & PCI_USBREV_MASK) { case PCI_USBREV_PRE_1_0: sc->sc_bus.usbrev = USBREV_PRE_1_0; break; case PCI_USBREV_1_0: sc->sc_bus.usbrev = USBREV_1_0; break; default: sc->sc_bus.usbrev = USBREV_UNKNOWN; break; } err = bus_setup_intr(self, sc->irq_res, INTR_TYPE_BIO|INTR_MPSAFE, (void *)(void *)uhci_interrupt, sc, &sc->ih); if(err) { device_printf(self, "Could not setup irq, %d\n", err); sc->ih = NULL; goto error; } /* * Set the PIRQD enable bit and switch off all the others. We don't * want legacy support to interfere with us XXX Does this also mean * that the BIOS won't touch the keyboard anymore if it is connected * to the ports of the root hub? */ #ifdef USB_DEBUG if(pci_read_config(self, PCI_LEGSUP, 2) != PCI_LEGSUP_USBPIRQDEN) { device_printf(self, "LegSup = 0x%04x\n", pci_read_config(self, PCI_LEGSUP, 2)); } #endif pci_write_config(self, PCI_LEGSUP, PCI_LEGSUP_USBPIRQDEN, 2); err = uhci_init(sc); if(!err) { err = device_probe_and_attach(sc->sc_bus.bdev); } if(err) { device_printf(self, "USB init failed\n"); goto error; } return 0; error: uhci_pci_detach(self); return ENXIO; } int uhci_pci_detach(device_t self) { uhci_softc_t *sc = device_get_softc(self); if(sc->sc_bus.bdev) { device_delete_child(self, sc->sc_bus.bdev); sc->sc_bus.bdev = NULL; } /* * disable interrupts that might have been switched on in * uhci_init. */ if(sc->io_res) { mtx_lock(&sc->sc_bus.mtx); /* stop the controller */ uhci_reset(sc); mtx_unlock(&sc->sc_bus.mtx); } pci_disable_busmaster(self); if(sc->irq_res && sc->ih) { int err = bus_teardown_intr(self, sc->irq_res, sc->ih); if(err) { /* XXX or should we panic? */ device_printf(self, "Could not tear down irq, %d\n", err); } sc->ih = NULL; } if(sc->irq_res) { bus_release_resource(self, SYS_RES_IRQ, 0, sc->irq_res); sc->irq_res = NULL; } if(sc->io_res) { bus_release_resource(self, SYS_RES_IOPORT, PCI_UHCI_BASE_REG, sc->io_res); sc->io_res = NULL; } if(sc->sc_bus.dma_tag) { usbd_dma_tag_free(sc->sc_bus.dma_tag); } mtx_destroy(&sc->sc_bus.mtx); usbd_mem_free(sc, sizeof(*sc)); device_set_softc(self, NULL); return 0; } static driver_t uhci_driver = { .name = "uhci", .methods = (device_method_t []) { /* device interface */ DEVMETHOD(device_probe, uhci_pci_probe), DEVMETHOD(device_attach, uhci_pci_attach), DEVMETHOD(device_detach, uhci_pci_detach), DEVMETHOD(device_suspend, uhci_pci_suspend), DEVMETHOD(device_resume, uhci_pci_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), {0, 0} }, .size = 0, }; static devclass_t uhci_devclass; DRIVER_MODULE(uhci, pci, uhci_driver, uhci_devclass, 0, 0); DRIVER_MODULE(uhci, cardbus, uhci_driver, uhci_devclass, 0, 0); pwcbsd/usb/uhid.c000644 000423 000000 00000046553 10551670754 014513 0ustar00luigiwheel000000 000000 /* $NetBSD: uhid.c,v 1.46 2001/11/13 06:24:55 lukem Exp $ */ /* Also already merged from NetBSD: * $NetBSD: uhid.c,v 1.54 2002/09/23 05:51:21 simonb Exp $ */ #include __FBSDID("$FreeBSD: src/sys/dev/usb/uhid.c,v 1.83 2006/09/07 00:06:41 imp Exp $"); /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" #ifdef USB_DEBUG #define DPRINTF(n,fmt,...) \ do { if (uhid_debug > (n)) { \ printf("%s: " fmt, __FUNCTION__,## __VA_ARGS__); } } while (0) static int uhid_debug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, uhid, CTLFLAG_RW, 0, "USB uhid"); SYSCTL_INT(_hw_usb_uhid, OID_AUTO, debug, CTLFLAG_RW, &uhid_debug, 0, "uhid debug level"); #else #define DPRINTF(...) #endif /* temporary compile hacks for old USB systems: */ #ifndef UQ_HID_IGNORE #define UQ_HID_IGNORE 0 #endif #ifndef USB_PRODUCT_WACOM_GRAPHIRE3_4X5 #define USB_PRODUCT_WACOM_GRAPHIRE3_4X5 0 #endif #define UHID_N_TRANSFER 5 /* units */ #define UHID_BSIZE 1024 /* bytes, buffer size */ #define UHID_FRAME_NUM 50 /* bytes, frame number */ struct uhid_softc { struct usb_cdev sc_cdev; struct mtx sc_mtx; struct usbd_xfer * sc_xfer[UHID_N_TRANSFER]; void * sc_repdesc_ptr; u_int32_t sc_isize; u_int32_t sc_osize; u_int32_t sc_fsize; u_int32_t sc_repdesc_size; u_int32_t sc_transfer_len; u_int8_t sc_transfer_buf[sizeof(usb_device_request_t) + UHID_BSIZE]; u_int8_t sc_iface_no; u_int8_t sc_iid; u_int8_t sc_oid; u_int8_t sc_fid; u_int8_t sc_flags; #define UHID_FLAG_IMMED 0x01 /* set if read should be immediate */ #define UHID_FLAG_INTR_STALL 0x02 /* set if interrupt transfer stalled */ #define UHID_FLAG_STATIC_DESC 0x04 /* set if report descriptors are static */ #define UHID_FLAG_COMMAND_ERR 0x08 /* set if control transfer had an error */ }; static u_int8_t uhid_xb360gp_report_descr[] = { UHID_XB360GP_REPORT_DESCR() }; static u_int8_t uhid_graphire_report_descr[] = { UHID_GRAPHIRE_REPORT_DESCR() }; static u_int8_t uhid_graphire3_4x5_report_descr[] = { UHID_GRAPHIRE3_4X5_REPORT_DESCR() }; static void uhid_intr_callback(struct usbd_xfer *xfer) { struct uhid_softc *sc = xfer->priv_sc; struct usbd_mbuf *m; USBD_CHECK_STATUS(xfer); tr_transferred: DPRINTF(0, "transferred!\n"); if (xfer->actlen >= sc->sc_isize) { usb_cdev_put_data(&(sc->sc_cdev), xfer->buffer, sc->sc_isize, 1); } else { /* ignore it */ DPRINTF(0, "ignored short transfer, " "%d bytes\n", xfer->actlen); } tr_setup: if (sc->sc_flags & UHID_FLAG_INTR_STALL) { usbd_transfer_start(sc->sc_xfer[1]); } else { USBD_IF_POLL(&(sc->sc_cdev.sc_rdq_free), m); if (m) { usbd_start_hardware(xfer); } } return; tr_error: if (xfer->error != USBD_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= UHID_FLAG_INTR_STALL; usbd_transfer_start(sc->sc_xfer[1]); } return; } static void uhid_intr_clear_stall_callback(struct usbd_xfer *xfer) { struct uhid_softc *sc = xfer->priv_sc; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, sc->sc_xfer[0]); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, sc->sc_xfer[0]); sc->sc_flags &= ~UHID_FLAG_INTR_STALL; usbd_transfer_start(sc->sc_xfer[0]); return; tr_error: /* bomb out */ sc->sc_flags &= ~UHID_FLAG_INTR_STALL; usb_cdev_put_data_error(&(sc->sc_cdev)); return; } static void uhid_write_callback(struct usbd_xfer *xfer) { struct uhid_softc *sc = xfer->priv_sc; usb_device_request_t *req = xfer->buffer; u_int32_t size = sc->sc_osize; u_int32_t actlen; u_int8_t id; USBD_CHECK_STATUS(xfer); tr_transferred: tr_setup: /* try to extract the ID byte */ if (sc->sc_oid) { if (usb_cdev_get_data(&(sc->sc_cdev), &id, 1, &actlen, 0)) { if (actlen != 1) { goto tr_error; } } else { return; } if (size) { size--; } } else { id = 0; } if (usb_cdev_get_data(&(sc->sc_cdev), req->bData, UHID_BSIZE, &actlen, 1)) { if (actlen != size) { goto tr_error; } usbd_fill_set_report (req, sc->sc_iface_no, UHID_OUTPUT_REPORT, id, size); xfer->length = sizeof(*req) + size; usbd_start_hardware(xfer); } return; tr_error: /* bomb out */ usb_cdev_get_data_error(&(sc->sc_cdev)); return; } static void uhid_read_callback(struct usbd_xfer *xfer) { struct uhid_softc *sc = xfer->priv_sc; usb_device_request_t *req = xfer->buffer; struct usbd_mbuf *m; USBD_CHECK_STATUS(xfer); tr_transferred: usb_cdev_put_data(&(sc->sc_cdev), req->bData, sc->sc_isize, 1); return; tr_setup: USBD_IF_POLL(&(sc->sc_cdev.sc_rdq_free), m); if (m) { usbd_fill_get_report (req, sc->sc_iface_no, UHID_INPUT_REPORT, sc->sc_iid, sc->sc_isize); xfer->length = sizeof(*req) + sc->sc_isize; usbd_start_hardware(xfer); } return; tr_error: /* bomb out */ usb_cdev_put_data_error(&(sc->sc_cdev)); return; } static void uhid_ioctl_callback(struct usbd_xfer *xfer) { struct uhid_softc *sc = xfer->priv_sc; USBD_CHECK_STATUS(xfer); tr_transferred: bcopy(xfer->buffer, sc->sc_transfer_buf, sc->sc_transfer_len); sc->sc_flags &= ~UHID_FLAG_COMMAND_ERR; usb_cdev_wakeup(&(sc->sc_cdev)); return; tr_error: DPRINTF(0, "error=%s\n", usbd_errstr(xfer->error)); sc->sc_flags |= UHID_FLAG_COMMAND_ERR; usb_cdev_wakeup(&(sc->sc_cdev)); return; tr_setup: bcopy(sc->sc_transfer_buf, xfer->buffer, sc->sc_transfer_len); xfer->length = sc->sc_transfer_len; usbd_start_hardware(xfer); return; } static const struct usbd_config uhid_config[UHID_N_TRANSFER] = { [0] = { .type = UE_INTERRUPT, .endpoint = -1, /* any */ .direction = UE_DIR_IN, .flags = USBD_SHORT_XFER_OK, .bufsize = 0, /* use wMaxPacketSize */ .callback = &uhid_intr_callback, }, [1] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .callback = &uhid_intr_clear_stall_callback, .timeout = 1000, /* 1 second */ }, [2] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t) + UHID_BSIZE, .callback = &uhid_write_callback, .timeout = 1000, /* 1 second */ }, [3] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t) + UHID_BSIZE, .callback = &uhid_read_callback, .timeout = 1000, /* 1 second */ }, [4] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t) + UHID_BSIZE, .callback = &uhid_ioctl_callback, .timeout = 1000, /* 1 second */ }, }; static void uhid_start_read(struct usb_cdev *cdev) { struct uhid_softc *sc = cdev->sc_priv_ptr; if (sc->sc_flags & UHID_FLAG_IMMED) { usbd_transfer_start(sc->sc_xfer[3]); } else { usbd_transfer_start(sc->sc_xfer[0]); } return; } static void uhid_stop_read(struct usb_cdev *cdev) { struct uhid_softc *sc = cdev->sc_priv_ptr; usbd_transfer_stop(sc->sc_xfer[3]); usbd_transfer_stop(sc->sc_xfer[0]); return; } static void uhid_start_write(struct usb_cdev *cdev) { struct uhid_softc *sc = cdev->sc_priv_ptr; usbd_transfer_start(sc->sc_xfer[2]); return; } static void uhid_stop_write(struct usb_cdev *cdev) { struct uhid_softc *sc = cdev->sc_priv_ptr; usbd_transfer_stop(sc->sc_xfer[2]); return; } static int32_t uhid_do_control_transfer(struct uhid_softc *sc, int32_t fflags) { int32_t error; sc->sc_flags |= UHID_FLAG_COMMAND_ERR; usbd_transfer_start(sc->sc_xfer[4]); error = usb_cdev_sleep(&(sc->sc_cdev), fflags, 0); usbd_transfer_stop(sc->sc_xfer[4]); if (error) { return error; } if (sc->sc_flags & UHID_FLAG_COMMAND_ERR) { return ENXIO; } return 0; } static int32_t uhid_get_report(struct uhid_softc *sc, int32_t fflags, u_int8_t type, u_int8_t id, void *data, u_int16_t len) { usb_device_request_t *req = (void *)(sc->sc_transfer_buf); int error; if (len > UHID_BSIZE) { len = UHID_BSIZE; } usbd_fill_get_report (req, sc->sc_iface_no, type, id, len); sc->sc_transfer_len = sizeof(*req) + len; error = uhid_do_control_transfer(sc, fflags); if (data) { bcopy(req->bData, data, len); } return error; } static int32_t uhid_set_report(struct uhid_softc *sc, int32_t fflags, u_int8_t type, u_int8_t id, void *data, u_int16_t len) { usb_device_request_t *req = (void *)(sc->sc_transfer_buf); if (len > UHID_BSIZE) { len = UHID_BSIZE; } usbd_fill_set_report (req, sc->sc_iface_no, type, id, len); bcopy(data, req->bData, len); sc->sc_transfer_len = sizeof(*req) + len; return uhid_do_control_transfer(sc, fflags); } static int32_t uhid_open(struct usb_cdev *cdev, int32_t fflags, int32_t devtype, struct thread *td) { struct uhid_softc *sc = cdev->sc_priv_ptr; if (fflags & FREAD) { /* reset flags */ sc->sc_flags &= ~UHID_FLAG_IMMED; } return 0; } static int32_t uhid_ioctl(struct usb_cdev *cdev, u_long cmd, caddr_t addr, int32_t fflags, struct thread *td) { struct uhid_softc *sc = cdev->sc_priv_ptr; struct usb_ctl_report_desc *rd; struct usb_ctl_report *re; u_int32_t size; int32_t error = 0; u_int8_t id; switch (cmd) { case USB_GET_REPORT_DESC: rd = (void *)addr; size = min(sc->sc_repdesc_size, sizeof(rd->ucrd_data)); rd->ucrd_size = size; bcopy(sc->sc_repdesc_ptr, rd->ucrd_data, size); break; case USB_SET_IMMED: if (!(fflags & FREAD)) { error = EPERM; goto done; } if (*(int *)addr) { /* do a test read */ error = uhid_get_report(sc, fflags, UHID_INPUT_REPORT, sc->sc_iid, NULL, sc->sc_isize); if (error) { goto done; } sc->sc_flags |= UHID_FLAG_IMMED; } else { sc->sc_flags &= ~UHID_FLAG_IMMED; } break; case USB_GET_REPORT: if (!(fflags & FREAD)) { error = EPERM; goto done; } re = (void *)addr; switch (re->ucr_report) { case UHID_INPUT_REPORT: size = sc->sc_isize; id = sc->sc_iid; break; case UHID_OUTPUT_REPORT: size = sc->sc_osize; id = sc->sc_oid; break; case UHID_FEATURE_REPORT: size = sc->sc_fsize; id = sc->sc_fid; break; default: error = EINVAL; goto done; } error = uhid_get_report(sc, fflags, re->ucr_report, id, re->ucr_data, size); if (error) { goto done; } break; case USB_SET_REPORT: if (!(fflags & FWRITE)) { error = EPERM; goto done; } re = (void *)addr; switch (re->ucr_report) { case UHID_INPUT_REPORT: size = sc->sc_isize; id = sc->sc_iid; break; case UHID_OUTPUT_REPORT: size = sc->sc_osize; id = sc->sc_oid; break; case UHID_FEATURE_REPORT: size = sc->sc_fsize; id = sc->sc_fid; break; default: return (EINVAL); } error = uhid_set_report(sc, fflags, re->ucr_report, id, re->ucr_data, size); if (error) { goto done; } break; case USB_GET_REPORT_ID: *(int *)addr = 0; /* XXX: we only support reportid 0? */ break; default: error = EINVAL; break; } done: return error; } static device_probe_t uhid_probe; static device_attach_t uhid_attach; static device_detach_t uhid_detach; static int uhid_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); usb_interface_descriptor_t *id; DPRINTF(10, "\n"); if (uaa->iface == NULL) { return UMATCH_NONE; } id = usbd_get_interface_descriptor(uaa->iface); if (id == NULL) { return UMATCH_NONE; } if (id->bInterfaceClass != UICLASS_HID) { /* the Xbox 360 gamepad doesn't use the HID class */ if ((id->bInterfaceClass != UICLASS_VENDOR) || (id->bInterfaceSubClass != UISUBCLASS_XBOX360_CONTROLLER) || (id->bInterfaceProtocol != UIPROTO_XBOX360_GAMEPAD)) { return UMATCH_NONE; } } if (usbd_get_quirks(uaa->device)->uq_flags & UQ_HID_IGNORE) { return UMATCH_NONE; } return UMATCH_IFACECLASS_GENERIC; } static int uhid_attach(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct uhid_softc *sc = device_get_softc(dev); usb_interface_descriptor_t *id = usbd_get_interface_descriptor(uaa->iface); const char * p_buf[2]; int32_t unit = device_get_unit(dev); int32_t error = 0; char buf[16]; DPRINTF(10, "sc=%p\n", sc); if (sc == NULL) { return ENOMEM; } usbd_set_desc(dev, uaa->device); mtx_init(&(sc->sc_mtx), "uhid lock", NULL, MTX_DEF|MTX_RECURSE); sc->sc_iface_no = uaa->iface->idesc->bInterfaceNumber; error = usbd_transfer_setup(uaa->device, uaa->iface_index, sc->sc_xfer, uhid_config, UHID_N_TRANSFER, sc, &(sc->sc_mtx)); if (error) { DPRINTF(0, "error=%s\n", usbd_errstr(error)) ; goto detach; } if (uaa->vendor == USB_VENDOR_WACOM) { /* the report descriptor for the Wacom Graphire is broken */ if (uaa->product == USB_PRODUCT_WACOM_GRAPHIRE) { sc->sc_repdesc_size = sizeof(uhid_graphire_report_descr); sc->sc_repdesc_ptr = uhid_graphire_report_descr; sc->sc_flags |= UHID_FLAG_STATIC_DESC; } else if (uaa->product == USB_PRODUCT_WACOM_GRAPHIRE3_4X5) { static u_int8_t reportbuf[] = { 2, 2, 2 }; /* * The Graphire3 needs 0x0202 to be written to * feature report ID 2 before it'll start * returning digitizer data. */ error = usbreq_set_report (uaa->device, uaa->iface_index, UHID_FEATURE_REPORT, 2, reportbuf, sizeof(reportbuf)); if (error) { DPRINTF(0, "set report failed, error=%s (ignored)\n", usbd_errstr(error)); } sc->sc_repdesc_size = sizeof(uhid_graphire3_4x5_report_descr); sc->sc_repdesc_ptr = uhid_graphire3_4x5_report_descr; sc->sc_flags |= UHID_FLAG_STATIC_DESC; } } else if ((id->bInterfaceClass == UICLASS_VENDOR) && (id->bInterfaceSubClass == UISUBCLASS_XBOX360_CONTROLLER) && (id->bInterfaceProtocol == UIPROTO_XBOX360_GAMEPAD)) { /* the Xbox 360 gamepad has no report descriptor */ sc->sc_repdesc_size = sizeof(uhid_xb360gp_report_descr); sc->sc_repdesc_ptr = uhid_xb360gp_report_descr; sc->sc_flags |= UHID_FLAG_STATIC_DESC; } if (sc->sc_repdesc_ptr == NULL) { error = usbreq_read_report_desc (uaa->device, uaa->iface_index, &(sc->sc_repdesc_ptr), &(sc->sc_repdesc_size), M_USBDEV); if (error) { device_printf(dev, "no report descriptor\n"); goto detach; } } error = usbreq_set_idle(uaa->device, uaa->iface_index, 0, 0); if (error) { DPRINTF(0, "set idle failed, error=%s (ignored)\n", usbd_errstr(error)); } sc->sc_isize = hid_report_size (sc->sc_repdesc_ptr, sc->sc_repdesc_size, hid_input, &sc->sc_iid); sc->sc_osize = hid_report_size (sc->sc_repdesc_ptr, sc->sc_repdesc_size, hid_output, &sc->sc_oid); sc->sc_fsize = hid_report_size (sc->sc_repdesc_ptr, sc->sc_repdesc_size, hid_feature, &sc->sc_fid); if (sc->sc_isize > UHID_BSIZE) { DPRINTF(0, "input size is too large, " "%d bytes (truncating)\n", sc->sc_isize); sc->sc_isize = UHID_BSIZE; } if (sc->sc_osize > UHID_BSIZE) { DPRINTF(0, "output size is too large, " "%d bytes (truncating)\n", sc->sc_osize); sc->sc_osize = UHID_BSIZE; } if (sc->sc_fsize > UHID_BSIZE) { DPRINTF(0, "feature size is too large, " "%d bytes (truncating)\n", sc->sc_fsize); sc->sc_fsize = UHID_BSIZE; } snprintf(buf, sizeof(buf), "uhid%d", unit); p_buf[0] = buf; p_buf[1] = NULL; sc->sc_cdev.sc_start_read = &uhid_start_read; sc->sc_cdev.sc_start_write = &uhid_start_write; sc->sc_cdev.sc_stop_read = &uhid_stop_read; sc->sc_cdev.sc_stop_write = &uhid_stop_write; sc->sc_cdev.sc_open = &uhid_open; sc->sc_cdev.sc_ioctl = &uhid_ioctl; sc->sc_cdev.sc_flags |= (USB_CDEV_FLAG_FWD_SHORT| USB_CDEV_FLAG_WAKEUP_RD_IMMED| USB_CDEV_FLAG_WAKEUP_WR_IMMED); /* make the buffers one byte larger than maximum so * that one can detect too large read/writes and * short transfers: */ error = usb_cdev_attach(&(sc->sc_cdev), sc, &(sc->sc_mtx), p_buf, UID_ROOT, GID_OPERATOR, 0644, sc->sc_isize+1, UHID_FRAME_NUM, sc->sc_osize+1, UHID_FRAME_NUM); if (error) { goto detach; } return 0; /* success */ detach: uhid_detach(dev); return ENOMEM; } static int uhid_detach(device_t dev) { struct uhid_softc *sc = device_get_softc(dev); usb_cdev_detach(&(sc->sc_cdev)); usbd_transfer_unsetup(sc->sc_xfer, UHID_N_TRANSFER); if (sc->sc_repdesc_ptr) { if (!(sc->sc_flags & UHID_FLAG_STATIC_DESC)) { free(sc->sc_repdesc_ptr, M_USBDEV); } } mtx_destroy(&(sc->sc_mtx)); return 0; } static devclass_t uhid_devclass; static device_method_t uhid_methods[] = { DEVMETHOD(device_probe, uhid_probe), DEVMETHOD(device_attach, uhid_attach), DEVMETHOD(device_detach, uhid_detach), { 0, 0 } }; static driver_t uhid_driver = { .name = "uhid", .methods = uhid_methods, .size = sizeof(struct uhid_softc), }; DRIVER_MODULE(uhid, uhub, uhid_driver, uhid_devclass, usbd_driver_load, 0); MODULE_DEPEND(uhid, usb, 1, 1, 1); pwcbsd/usb/uhub.c000644 000423 000000 00000047266 10551670754 014527 0ustar00luigiwheel000000 000000 /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * USB spec: http://www.usb.org/developers/docs/usbspec.zip */ #include #include #include #include #include #include #include #include #include __FBSDID("$FreeBSD: src/sys/dev/usb/uhub.c $"); #define UHUB_INTR_INTERVAL 250 /* ms */ #ifdef USB_DEBUG #undef DPRINTF #undef DPRINTFN #define DPRINTF(x) { if (uhubdebug) { printf("%s: ", __FUNCTION__); printf x; } } #define DPRINTFN(n,x) { if (uhubdebug>(n)) { printf("%s: ", __FUNCTION__); printf x; } } int uhubdebug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, uhub, CTLFLAG_RW, 0, "USB uhub"); SYSCTL_INT(_hw_usb_uhub, OID_AUTO, debug, CTLFLAG_RW, &uhubdebug, 0, "uhub debug level"); #endif struct uhub_softc { device_t sc_dev; /* base device */ struct usbd_device * sc_hub; /* USB device */ struct usbd_xfer * sc_xfer[2]; /* interrupt xfer */ u_int8_t sc_running; }; #define UHUB_PROTO(sc) ((sc)->sc_hub->ddesc.bDeviceProtocol) #define UHUB_IS_HIGH_SPEED(sc) (UHUB_PROTO(sc) != UDPROTO_FSHUB) #define UHUB_IS_SINGLE_TT(sc) (UHUB_PROTO(sc) == UDPROTO_HSHUBSTT) /* prototypes for type checking: */ static device_probe_t uhub_probe; static device_attach_t uhub_attach; static device_detach_t uhub_detach; static bus_driver_added_t uhub_driver_added; static bus_child_location_str_t uhub_child_location_string; static bus_child_pnpinfo_str_t uhub_child_pnpinfo_string; /* * Hub interrupt. * This an indication that some port has changed status. * Notify the bus event handler thread that we need * to be explored again. */ static void uhub_interrupt(struct usbd_xfer *xfer) { USBD_CHECK_STATUS(xfer); tr_transferred: usb_needs_explore(((struct uhub_softc *)(xfer->priv_sc))->sc_hub); tr_setup: tr_error: /* re-transfer xfer->buffer; * xfer->length is unchanged */ usbd_start_hardware(xfer); return; } static usbd_status uhub_explore(struct usbd_device *udev) { usb_hub_descriptor_t *hd = &udev->hub->hubdesc; struct uhub_softc *sc = udev->hub->hubsoftc; struct usbd_port *up; usbd_status err; int speed; int port; int change, status; DPRINTFN(10, ("udev=%p addr=%d\n", udev, udev->address)); if (!sc->sc_running) { return (USBD_NOT_STARTED); } /* ignore hubs that are too deep */ if(udev->depth > USB_HUB_MAX_DEPTH) { return (USBD_TOO_DEEP); } for(port = 1; port <= hd->bNbrPorts; port++) { up = &udev->hub->ports[port-1]; err = usbreq_get_port_status(udev, port, &up->status); if (err) { DPRINTF(("get port status failed, " "error=%s\n", usbd_errstr(err))); continue; } status = UGETW(up->status.wPortStatus); change = UGETW(up->status.wPortChange); DPRINTFN(3,("%s: port %d status 0x%04x 0x%04x\n", device_get_nameunit(sc->sc_dev), port, status, change)); if(change & UPS_C_PORT_ENABLED) { DPRINTF(("C_PORT_ENABLED 0x%x\n", change)); usbreq_clear_port_feature(udev, port, UHF_C_PORT_ENABLE); if(change & UPS_C_CONNECT_STATUS) { /* ignore the port error * if the device vanished */ } else if(status & UPS_PORT_ENABLED) { device_printf(sc->sc_dev, "illegal enable change, port %d\n", port); } else { /* port error condition */ if(up->restartcnt) /* no message first time */ { device_printf(sc->sc_dev, "port error, restarting " "port %d\n", port); } if(up->restartcnt++ < USBD_RESTART_MAX) goto disconnect; else device_printf(sc->sc_dev, "port error, giving up " "port %d\n", port); } } if(!(change & UPS_C_CONNECT_STATUS)) { DPRINTFN(3,("port=%d !C_CONNECT_" "STATUS\n", port)); /* no status change, just do recursive explore */ if(up->device != NULL) { if(up->device->hub != NULL) { (up->device->hub->explore)(up->device); } else { /* allow drivers to be hot-plugged */ if(up->last_refcount != usb_driver_added_refcount) { usbd_probe_and_attach (sc->sc_dev, port, up); } } } #if 0 && defined(DIAGNOSTIC) if((up->device == NULL) && (status & UPS_CURRENT_CONNECT_STATUS)) { device_printf(sc->sc_dev, "connected, no device\n"); } #endif continue; } /* we have a connect status change, handle it */ DPRINTF(("status change hub=%d port=%d\n", udev->address, port)); usbreq_clear_port_feature(udev, port, UHF_C_PORT_CONNECTION); /*usbreq_clear_port_feature(udev, port, UHF_C_PORT_ENABLE);*/ /* * If there is already a device on the port the change status * must mean that is has disconnected. Looking at the * current connect status is not enough to figure this out * since a new unit may have been connected before we handle * the disconnect. */ disconnect: if(up->device != NULL) { /* disconnected */ DPRINTF(("device addr=%d disappeared " "on port %d\n", up->device->address, port)); usbd_free_device(up, 1); usbreq_clear_port_feature(udev, port, UHF_C_PORT_CONNECTION); } if(!(status & UPS_CURRENT_CONNECT_STATUS)) { /* nothing connected, just ignore it */ DPRINTFN(3,("port=%d !CURRENT_CONNECT_STATUS\n", port)); continue; } /* connected */ if(!(status & UPS_PORT_POWER)) { device_printf(sc->sc_dev, "strange, connected port %d " "has no power\n", port); } /* wait for maximum device power up time */ usbd_delay_ms(udev, USB_PORT_POWERUP_DELAY); /* reset port, which implies enabling it */ if(usbreq_reset_port(udev, port, &up->status)) { device_printf(sc->sc_dev, "port %d reset failed\n", port); continue; } /* get port status again, it might have changed during reset */ err = usbreq_get_port_status(udev, port, &up->status); if(err) { DPRINTF(("get port status failed, " "error=%s\n", usbd_errstr(err))); continue; } status = UGETW(up->status.wPortStatus); change = UGETW(up->status.wPortChange); if(!(status & UPS_CURRENT_CONNECT_STATUS)) { /* nothing connected, just ignore it */ #ifdef DIAGNOSTIC device_printf(sc->sc_dev, "port %d, device disappeared " "after reset\n", port); #endif continue; } /* figure out device speed */ speed = (status & UPS_HIGH_SPEED) ? USB_SPEED_HIGH : (status & UPS_LOW_SPEED) ? USB_SPEED_LOW : USB_SPEED_FULL; /* get device info and set its address */ err = usbd_new_device(sc->sc_dev, udev->bus, udev->depth + 1, speed, port, up); /* XXX retry a few times? */ if(err) { DPRINTFN(-1,("usb_new_device failed, " "error=%s\n", usbd_errstr(err))); /* Avoid addressing problems by disabling. */ /* usbd_reset_port(udev, port, &up->status); */ /* * The unit refused to accept a new address, or had * some other serious problem. Since we cannot leave * at 0 we have to disable the port instead. */ device_printf(sc->sc_dev, "device problem (%s), " "disabling port %d\n", usbd_errstr(err), port); usbreq_clear_port_feature(udev, port, UHF_PORT_ENABLE); } else { /* the port set up succeeded, reset error count */ up->restartcnt = 0; if(up->device->hub) { (up->device->hub->explore)(up->device); } } } return (USBD_NORMAL_COMPLETION); } static int uhub_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); usb_device_descriptor_t *dd = usbd_get_device_descriptor(uaa->device); DPRINTFN(5,("dd=%p\n", dd)); /* * the subclass for hubs, is ignored, * because it is 0 for some * and 1 for others */ if((uaa->iface == NULL) && (dd->bDeviceClass == UDCLASS_HUB)) { return (UMATCH_DEVCLASS_DEVSUBCLASS); } return (UMATCH_NONE); } static int uhub_attach(device_t dev) { struct uhub_softc *sc = device_get_softc(dev); struct usb_attach_arg *uaa = device_get_ivars(dev); struct usbd_device *udev = uaa->device; struct usbd_hub *hub; usbd_status err; usb_device_request_t req; usb_hub_descriptor_t hubdesc; int port, nports, removable, pwrdly; char devinfo[256]; DPRINTFN(1,("\n")); sc->sc_hub = udev; sc->sc_dev = dev; err = usbd_set_config_index(udev, 0, 1); if(err) { DPRINTF(("%s: configuration failed, error=%s\n", device_get_nameunit(dev), usbd_errstr(err))); goto error; } /* NOTE: "usbd_set_config_index()" will change * variables in "udev" ! */ DPRINTFN(1,("depth=%d selfpowered=%d, parent=%p, " "parent->selfpowered=%d\n", udev->depth, udev->self_powered, udev->powersrc->parent, udev->powersrc->parent ? udev->powersrc->parent->self_powered : 0)); if(udev->depth > USB_HUB_MAX_DEPTH) { device_printf(dev, "hub depth (%d) exceeded, hub ignored\n", USB_HUB_MAX_DEPTH); goto error; } if(!udev->self_powered && (udev->powersrc->parent != NULL) && (!udev->powersrc->parent->self_powered)) { device_printf(dev, "bus powered hub connected to bus powered hub, " "ignored\n"); goto error; } /* get hub descriptor */ DPRINTFN(1,("getting hub descriptor\n")); req.bmRequestType = UT_READ_CLASS_DEVICE; req.bRequest = UR_GET_DESCRIPTOR; USETW2(req.wValue, UDESC_HUB, 0); USETW(req.wIndex, 0); USETW(req.wLength, USB_HUB_DESCRIPTOR_SIZE); err = usbd_do_request(udev, &req, &hubdesc); nports = hubdesc.bNbrPorts; if(!err && (nports >= 8)) { u_int16_t len = (USB_HUB_DESCRIPTOR_SIZE-1) + ((nports+7) / 8); USETW(req.wLength, len); err = usbd_do_request(udev, &req, &hubdesc); } if(err) { DPRINTF(("%s: getting hub descriptor failed, error=%s\n", device_get_nameunit(dev), usbd_errstr(err))); goto error; } if(hubdesc.bNbrPorts != nports) { DPRINTF(("%s: number of ports changed!\n", device_get_nameunit(dev))); goto error; } if(nports == 0) { DPRINTF(("%s: portless HUB!\n", device_get_nameunit(dev))); goto error; } udev->hub = malloc((sizeof(hub[0]) + (sizeof(hub[0].ports[0]) * nports)), M_USBDEV, M_NOWAIT|M_ZERO); if(udev->hub == NULL) { goto error; } hub = udev->hub; hub->hubsoftc = sc; hub->explore = uhub_explore; hub->hubdesc = hubdesc; static const struct usbd_config usbd_config[2] = { [0] = { .type = UE_INTERRUPT, .endpoint = -1, /* any pipe number */ .direction = -1, /* any pipe direction */ .timeout = 0, .flags = USBD_SHORT_XFER_OK, .bufsize = 0x100 / 8, .callback = uhub_interrupt, .interval = UHUB_INTR_INTERVAL, }, [1] = { .type = UE_CONTROL, .endpoint = 0, .direction = -1, .timeout = USBD_DEFAULT_TIMEOUT, .flags = 0, .bufsize = sizeof(usb_device_request_t), .callback = &usbd_clearstall_callback, }, }; /* set up interrupt pipe */ err = usbd_transfer_setup(udev, 0, &sc->sc_xfer[0], &usbd_config[0], 2, sc, NULL); if(err) { device_printf(dev, "cannot open interrupt pipe\n"); goto error; } /* setup clear stall */ sc->sc_xfer[0]->clearstall_xfer = sc->sc_xfer[1]; usbd_transfer_start_safe(sc->sc_xfer[0]); /* wait with power off for a while */ usbd_delay_ms(udev, USB_POWER_DOWN_TIME); usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, udev, dev); /* * To have the best chance of success we do things in the exact same * order as Windoze98. This should not be necessary, but some * devices do not follow the USB specs to the letter. * * These are the events on the bus when a hub is attached: * Get device and config descriptors (see attach code) * Get hub descriptor (see above) * For all ports * turn on power * wait for power to become stable * (all below happens in explore code) * For all ports * clear C_PORT_CONNECTION * For all ports * get port status * if device connected * wait 100 ms * turn on reset * wait * clear C_PORT_RESET * get port status * proceed with device attachment */ /* XXX should check for none, individual, or ganged power? */ removable = 0; pwrdly = (hubdesc.bPwrOn2PwrGood * UHD_PWRON_FACTOR) + USB_EXTRA_POWER_UP_TIME; for(port = 1; port <= nports; port++) { /* set up data structures */ struct usbd_port *up = &hub->ports[port-1]; up->device = NULL; up->parent = udev; up->portno = port; /* self powered hub, give ports maximum current */ up->power = (udev->self_powered) ? USB_MAX_POWER : USB_MIN_POWER ; up->restartcnt = 0; /* check if port is removable */ if(!UHD_NOT_REMOV(&hubdesc, port)) { removable++; } /* turn the power on */ err = usbreq_set_port_feature(udev, port, UHF_PORT_POWER); if(err) { device_printf(dev, "port %d power on failed, %s\n", port, usbd_errstr(err)); } DPRINTF(("turn on port %d power\n", port)); /* wait for stable power */ usbd_delay_ms(udev, pwrdly); } usbd_devinfo(udev, 1, devinfo, sizeof(devinfo)); device_set_desc_copy(dev, devinfo); device_printf(dev, "%s\n", devinfo); device_printf(dev, "%d port%s with %d " "removable, %s powered\n", nports, (nports != 1) ? "s" : "", removable, udev->self_powered ? "self" : "bus"); /* the usual exploration will finish the setup */ sc->sc_running = 1; return 0; error: if(udev->hub) { free(udev->hub, M_USBDEV); } udev->hub = NULL; return ENXIO; } /* * Called from process context when the hub is gone. * Detach all devices on active ports. */ static int uhub_detach(device_t dev) { struct uhub_softc *sc = device_get_softc(dev); struct usbd_hub *hub = sc->sc_hub->hub; struct usbd_port *up; int port, nports; DPRINTF(("sc=%port\n", sc)); if(hub == NULL) /* must be partially working */ { return (0); } nports = hub->hubdesc.bNbrPorts; for(port = 0; port < nports; port++) { up = &hub->ports[port]; if(up->device) { /* subdevices are not freed, because * the caller of uhub_detach() will * do that */ usbd_free_device(up, 0); } } usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_hub, sc->sc_dev); usbd_transfer_unsetup(&sc->sc_xfer[0], 2); free(hub, M_USBDEV); sc->sc_hub->hub = NULL; return (0); } static void uhub_driver_added(device_t dev, driver_t *driver) { usb_needs_probe_and_attach(); return; } static int uhub_child_location_string(device_t parent, device_t child, char *buf, size_t buflen) { struct uhub_softc *sc = device_get_softc(parent); struct usbd_hub *hub = sc->sc_hub->hub; struct usbd_device *udev; int port, nports, iface_index; mtx_lock(&usb_global_lock); nports = hub->hubdesc.bNbrPorts; for(port = 0; port < nports; port++) { udev = hub->ports[port].device; if(udev) { device_t * subdev = &udev->subdevs[0]; device_t * subdev_end = &udev->subdevs_end[0]; iface_index = 0; while(subdev < subdev_end) { if(subdev[0] == child) { goto found; } subdev++; iface_index++; } } } mtx_unlock(&usb_global_lock); DPRINTFN(0,("device not on hub\n")); if(buflen) { buf[0] = '\0'; } return 0; found: if(udev->probed == USBD_PROBED_IFACE_AND_FOUND) { snprintf(buf, buflen, "port=%i interface=%i", port, iface_index); } else { snprintf(buf, buflen, "port=%i", port); } mtx_unlock(&usb_global_lock); return (0); } static int uhub_child_pnpinfo_string(device_t parent, device_t child, char *buf, size_t buflen) { struct uhub_softc *sc = device_get_softc(parent); struct usbd_hub *hub = sc->sc_hub->hub; struct usbd_interface *iface; struct usbd_device *udev; int port, nports, iface_index; mtx_lock(&usb_global_lock); nports = hub->hubdesc.bNbrPorts; for(port = 0; port < nports; port++) { udev = hub->ports[port].device; if(udev) { device_t * subdev = &udev->subdevs[0]; device_t * subdev_end = &udev->subdevs_end[0]; iface_index = 0; while(subdev < subdev_end) { if(subdev[0] == child) { goto found; } subdev++; iface_index++; } } } mtx_unlock(&usb_global_lock); DPRINTFN(0,("device not on hub\n")); if(buflen) { buf[0] = '\0'; } return 0; found: iface = usbd_get_iface(udev, iface_index); if((udev->probed == USBD_PROBED_IFACE_AND_FOUND) && iface && iface->idesc) { snprintf(buf, buflen, "vendor=0x%04x product=0x%04x " "devclass=0x%02x devsubclass=0x%02x " "sernum=\"%s\" " "intclass=0x%02x intsubclass=0x%02x", UGETW(udev->ddesc.idVendor), UGETW(udev->ddesc.idProduct), udev->ddesc.bDeviceClass, udev->ddesc.bDeviceSubClass, &udev->serial[0], iface->idesc->bInterfaceClass, iface->idesc->bInterfaceSubClass); } else { snprintf(buf, buflen, "vendor=0x%04x product=0x%04x " "devclass=0x%02x devsubclass=0x%02x " "sernum=\"%s\"", UGETW(udev->ddesc.idVendor), UGETW(udev->ddesc.idProduct), udev->ddesc.bDeviceClass, udev->ddesc.bDeviceSubClass, &udev->serial[0]); } mtx_unlock(&usb_global_lock); return (0); } /* * driver instance for "hub" connected to "usb" * and "hub" connected to "hub" */ static devclass_t uhub_devclass; static driver_t uhub_driver = { .name = "uhub", .methods = (device_method_t []) { DEVMETHOD(device_probe, uhub_probe), DEVMETHOD(device_attach, uhub_attach), DEVMETHOD(device_detach, uhub_detach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(bus_child_location_str, uhub_child_location_string), DEVMETHOD(bus_child_pnpinfo_str, uhub_child_pnpinfo_string), DEVMETHOD(bus_driver_added, uhub_driver_added), {0,0} }, .size = sizeof(struct uhub_softc) }; DRIVER_MODULE(uhub, usb, uhub_driver, uhub_devclass, 0, 0); MODULE_DEPEND(uhub, usb, 1, 1, 1); DRIVER_MODULE(uhub, uhub, uhub_driver, uhub_devclass, usbd_driver_load, 0); pwcbsd/usb/ukbd.c000644 000423 000000 00000106444 10551670754 014503 0ustar00luigiwheel000000 000000 /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * Modifications for SUN TYPE 6 USB Keyboard by * Jörg Peter Schley (jps@scxnet.de) */ /* * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf */ #include "opt_kbd.h" #include "opt_ukbd.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* the initial key map, accent map and fkey strings */ #ifdef UKBD_DFLT_KEYMAP #define KBD_DFLT_KEYMAP #include "ukbdmap.h" #endif __FBSDID("$FreeBSD: src/sys/dev/usb/ukbd.c,v 1.53 2006/02/28 03:34:06 emax Exp $"); #ifdef USB_DEBUG #define DPRINTF(n,fmt,...) \ do { if (ukbd_debug > (n)) { \ printf("%s: " fmt, __FUNCTION__,## __VA_ARGS__); } } while (0) static int ukbd_debug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, ukbd, CTLFLAG_RW, 0, "USB ukbd"); SYSCTL_INT(_hw_usb_ukbd, OID_AUTO, debug, CTLFLAG_RW, &ukbd_debug, 0, "ukbd debug level"); #else #define DPRINTF(...) #endif #define UPROTO_BOOT_KEYBOARD 1 #define UKBD_EMULATE_ATSCANCODE 1 #define UKBD_DRIVER_NAME "ukbd" #define UKBD_NMOD 8 /* units */ #define UKBD_NKEYCODE 6 /* units */ #define UKBD_N_TRANSFER 3 /* units */ #define UKBD_IN_BUF_SIZE (2*(UKBD_NMOD + (2*UKBD_NKEYCODE))) /* bytes */ #define UKBD_IN_BUF_FULL (UKBD_IN_BUF_SIZE / 2) /* bytes */ #define UKBD_NFKEY (sizeof(fkey_tab)/sizeof(fkey_tab[0])) /* units */ struct ukbd_data { u_int8_t modifiers; #define MOD_CONTROL_L 0x01 #define MOD_CONTROL_R 0x10 #define MOD_SHIFT_L 0x02 #define MOD_SHIFT_R 0x20 #define MOD_ALT_L 0x04 #define MOD_ALT_R 0x40 #define MOD_WIN_L 0x08 #define MOD_WIN_R 0x80 u_int8_t reserved; u_int8_t keycode[UKBD_NKEYCODE]; } __attribute__((__packed__)); struct ukbd_softc { keyboard_t sc_kbd; keymap_t sc_keymap; accentmap_t sc_accmap; fkeytab_t sc_fkeymap[UKBD_NFKEY]; struct __callout sc_callout; struct ukbd_data sc_ndata; struct ukbd_data sc_odata; struct usbd_device * sc_udev; struct usbd_interface * sc_iface; struct usbd_xfer * sc_xfer[UKBD_N_TRANSFER]; u_int32_t sc_ntime[UKBD_NKEYCODE]; u_int32_t sc_otime[UKBD_NKEYCODE]; u_int32_t sc_input[UKBD_IN_BUF_SIZE]; /* input buffer */ u_int32_t sc_time_ms; u_int32_t sc_composed_char; /* composed char code, if non-zero */ #ifdef UKBD_EMULATE_ATSCANCODE u_int32_t sc_buffered_char[2]; #endif u_int32_t sc_flags; /* flags */ #define UKBD_FLAG_COMPOSE 0x0001 #define UKBD_FLAG_POLLING 0x0002 #define UKBD_FLAG_SET_LEDS 0x0004 #define UKBD_FLAG_INTR_STALL 0x0008 #define UKBD_FLAG_ATTACHED 0x0010 #define UKBD_FLAG_GONE 0x0020 int32_t sc_mode; /* input mode (K_XLATE,K_RAW,K_CODE) */ int32_t sc_state; /* shift/lock key state */ int32_t sc_accents; /* accent key index (> 0) */ u_int16_t sc_inputs; u_int16_t sc_inputhead; u_int16_t sc_inputtail; u_int8_t sc_leds; u_int8_t sc_iface_index; }; #define KEY_ERROR 0x01 #define KEY_PRESS 0 #define KEY_RELEASE 0x400 #define KEY_INDEX(c) ((c) & 0xFF) #define SCAN_PRESS 0 #define SCAN_RELEASE 0x80 #define SCAN_PREFIX_E0 0x100 #define SCAN_PREFIX_E1 0x200 #define SCAN_PREFIX_CTL 0x400 #define SCAN_PREFIX_SHIFT 0x800 #define SCAN_PREFIX (SCAN_PREFIX_E0 | SCAN_PREFIX_E1 | \ SCAN_PREFIX_CTL | SCAN_PREFIX_SHIFT) #define SCAN_CHAR(c) ((c) & 0x7f) static const struct { u_int32_t mask, key; } ukbd_mods[UKBD_NMOD] = { { MOD_CONTROL_L, 0xe0 }, { MOD_CONTROL_R, 0xe4 }, { MOD_SHIFT_L, 0xe1 }, { MOD_SHIFT_R, 0xe5 }, { MOD_ALT_L, 0xe2 }, { MOD_ALT_R, 0xe6 }, { MOD_WIN_L, 0xe3 }, { MOD_WIN_R, 0xe7 }, }; #define NN 0 /* no translation */ /* * Translate USB keycodes to AT keyboard scancodes. */ /* * FIXME: Mac USB keyboard generates: * 0x53: keypad NumLock/Clear * 0x66: Power * 0x67: keypad = * 0x68: F13 * 0x69: F14 * 0x6a: F15 */ static const u_int8_t ukbd_trtab[256] = { 0, 0, 0, 0, 30, 48, 46, 32, /* 00 - 07 */ 18, 33, 34, 35, 23, 36, 37, 38, /* 08 - 0F */ 50, 49, 24, 25, 16, 19, 31, 20, /* 10 - 17 */ 22, 47, 17, 45, 21, 44, 2, 3, /* 18 - 1F */ 4, 5, 6, 7, 8, 9, 10, 11, /* 20 - 27 */ 28, 1, 14, 15, 57, 12, 13, 26, /* 28 - 2F */ 27, 43, 43, 39, 40, 41, 51, 52, /* 30 - 37 */ 53, 58, 59, 60, 61, 62, 63, 64, /* 38 - 3F */ 65, 66, 67, 68, 87, 88, 92, 70, /* 40 - 47 */ 104, 102, 94, 96, 103, 99, 101, 98, /* 48 - 4F */ 97, 100, 95, 69, 91, 55, 74, 78, /* 50 - 57 */ 89, 79, 80, 81, 75, 76, 77, 71, /* 58 - 5F */ 72, 73, 82, 83, 86, 107, 122, NN, /* 60 - 67 */ NN, NN, NN, NN, NN, NN, NN, NN, /* 68 - 6F */ NN, NN, NN, NN, 115, 108, 111, 113, /* 70 - 77 */ 109, 110, 112, 118, 114, 116, 117, 119, /* 78 - 7F */ 121, 120, NN, NN, NN, NN, NN, 115, /* 80 - 87 */ 112, 125, 121, 123, NN, NN, NN, NN, /* 88 - 8F */ NN, NN, NN, NN, NN, NN, NN, NN, /* 90 - 97 */ NN, NN, NN, NN, NN, NN, NN, NN, /* 98 - 9F */ NN, NN, NN, NN, NN, NN, NN, NN, /* A0 - A7 */ NN, NN, NN, NN, NN, NN, NN, NN, /* A8 - AF */ NN, NN, NN, NN, NN, NN, NN, NN, /* B0 - B7 */ NN, NN, NN, NN, NN, NN, NN, NN, /* B8 - BF */ NN, NN, NN, NN, NN, NN, NN, NN, /* C0 - C7 */ NN, NN, NN, NN, NN, NN, NN, NN, /* C8 - CF */ NN, NN, NN, NN, NN, NN, NN, NN, /* D0 - D7 */ NN, NN, NN, NN, NN, NN, NN, NN, /* D8 - DF */ 29, 42, 56, 105, 90, 54, 93, 106, /* E0 - E7 */ NN, NN, NN, NN, NN, NN, NN, NN, /* E8 - EF */ NN, NN, NN, NN, NN, NN, NN, NN, /* F0 - F7 */ NN, NN, NN, NN, NN, NN, NN, NN, /* F8 - FF */ }; /* prototypes */ static void ukbd_timeout(void *arg); static void ukbd_set_leds(struct ukbd_softc *sc, u_int8_t leds); static int ukbd_set_typematic(keyboard_t *kbd, int code); #ifdef UKBD_EMULATE_ATSCANCODE static int ukbd_key2scan(struct ukbd_softc *sc, int keycode, int shift, int up); #endif static u_int32_t ukbd_read_char(keyboard_t *kbd, int wait); static void ukbd_clear_state(keyboard_t *kbd); static int ukbd_ioctl(keyboard_t *kbd, u_long cmd, caddr_t arg); static int ukbd_enable(keyboard_t *kbd); static int ukbd_disable(keyboard_t *kbd); static void ukbd_interrupt(struct ukbd_softc *sc); static device_probe_t ukbd_probe; static device_attach_t ukbd_attach; static device_detach_t ukbd_detach; static device_resume_t ukbd_resume; static void ukbd_put_key(struct ukbd_softc *sc, u_int32_t key) { mtx_assert(&Giant, MA_OWNED); DPRINTF(0, "0x%02x (%d) %s\n", key, key, (key & KEY_RELEASE) ? "released" : "pressed"); if (sc->sc_inputs < UKBD_IN_BUF_SIZE) { sc->sc_input[sc->sc_inputtail] = key; ++(sc->sc_inputs); ++(sc->sc_inputtail); if (sc->sc_inputtail >= UKBD_IN_BUF_SIZE) { sc->sc_inputtail = 0; } } else { DPRINTF(0, "input buffer is full\n"); } return; } static int32_t ukbd_get_key(struct ukbd_softc *sc) { int32_t c; mtx_assert(&Giant, MA_OWNED); if (sc->sc_inputs == 0) { /* start transfer, if not already started */ usbd_transfer_start(sc->sc_xfer[0]); } if (sc->sc_flags & UKBD_FLAG_POLLING) { DPRINTF(1, "polling\n"); while (sc->sc_inputs == 0) { usbd_do_poll(sc->sc_udev); DELAY(1000); /* delay 1 ms */ sc->sc_time_ms ++; /* support repetition of keys: */ ukbd_interrupt(sc); } } if (sc->sc_inputs == 0) { c = -1; } else { c = sc->sc_input[sc->sc_inputhead]; --(sc->sc_inputs); ++(sc->sc_inputhead); if (sc->sc_inputhead >= UKBD_IN_BUF_SIZE) { sc->sc_inputhead = 0; } } return c; } static void ukbd_interrupt(struct ukbd_softc *sc) { u_int32_t n_mod; u_int32_t o_mod; u_int32_t now = sc->sc_time_ms; u_int32_t dtime; u_int32_t c; u_int8_t key; u_int8_t i; u_int8_t j; if (sc->sc_ndata.keycode[0] == KEY_ERROR) { goto done; } n_mod = sc->sc_ndata.modifiers; o_mod = sc->sc_odata.modifiers; if (n_mod != o_mod) { for (i = 0; i < UKBD_NMOD; i++) { if ((n_mod & ukbd_mods[i].mask) != (o_mod & ukbd_mods[i].mask)) { ukbd_put_key(sc, ukbd_mods[i].key | ((n_mod & ukbd_mods[i].mask) ? KEY_PRESS : KEY_RELEASE)); } } } /* Check for released keys. */ for (i = 0; i < UKBD_NKEYCODE; i++) { key = sc->sc_odata.keycode[i]; if (key == 0) { continue; } for (j = 0; j < UKBD_NKEYCODE; j++) { if (sc->sc_ndata.keycode[j] == 0) { continue; } if (key == sc->sc_ndata.keycode[j]) { goto rfound; } } ukbd_put_key(sc, key | KEY_RELEASE); rfound:; } /* Check for pressed keys. */ for (i = 0; i < UKBD_NKEYCODE; i++) { key = sc->sc_ndata.keycode[i]; if (key == 0) { continue; } sc->sc_ntime[i] = now + sc->sc_kbd.kb_delay1; for (j = 0; j < UKBD_NKEYCODE; j++) { if (sc->sc_odata.keycode[j] == 0) { continue; } if (key == sc->sc_odata.keycode[j]) { /* key is still pressed */ sc->sc_ntime[i] = sc->sc_otime[j]; dtime = (sc->sc_otime[j] - now); if (!(dtime & 0x80000000)) { /* time has not elapsed */ goto pfound; } sc->sc_ntime[i] = now + sc->sc_kbd.kb_delay2; break; } } ukbd_put_key(sc, key | KEY_PRESS); pfound:; } sc->sc_odata = sc->sc_ndata; bcopy(sc->sc_ntime, sc->sc_otime, sizeof(sc->sc_otime)); if (sc->sc_inputs == 0) { goto done; } if (sc->sc_flags & UKBD_FLAG_POLLING) { goto done; } if (KBD_IS_ACTIVE(&(sc->sc_kbd)) && KBD_IS_BUSY(&(sc->sc_kbd))) { /* let the callback function process the input */ (sc->sc_kbd.kb_callback.kc_func)(&(sc->sc_kbd), KBDIO_KEYINPUT, sc->sc_kbd.kb_callback.kc_arg); } else { /* read and discard the input, no one is waiting for it */ do { c = ukbd_read_char(&(sc->sc_kbd), 0); } while (c != NOKEY); } done: return; } static void ukbd_timeout(void *arg) { struct ukbd_softc *sc = arg; mtx_assert(&Giant, MA_OWNED); if (!(sc->sc_flags & UKBD_FLAG_POLLING)) { sc->sc_time_ms += 25; /* milliseconds */ } ukbd_interrupt(sc); __callout_reset(&(sc->sc_callout), hz / 40, &ukbd_timeout, sc); mtx_unlock(&Giant); return; } static void ukbd_clear_stall_callback(struct usbd_xfer *xfer) { struct ukbd_softc *sc = xfer->priv_sc; struct usbd_xfer *xfer_other = sc->sc_xfer[0]; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, xfer_other); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, xfer_other); sc->sc_flags &= ~UKBD_FLAG_INTR_STALL; usbd_transfer_start(xfer_other); return; tr_error: /* bomb out */ sc->sc_flags &= ~UKBD_FLAG_INTR_STALL; DPRINTF(0, "error=%s\n", usbd_errstr(xfer->error)); return; } static void ukbd_intr_callback(struct usbd_xfer *xfer) { struct ukbd_softc *sc = xfer->priv_sc; u_int8_t *buf = xfer->buffer; u_int16_t len = xfer->actlen; u_int8_t i; USBD_CHECK_STATUS(xfer); tr_transferred: DPRINTF(0, "actlen=%d bytes\n", len); if (len > sizeof(sc->sc_ndata)) { len = sizeof(sc->sc_ndata); } if (len) { bzero(&(sc->sc_ndata), sizeof(sc->sc_ndata)); bcopy(buf, &(sc->sc_ndata), len); #ifdef USB_DEBUG if (sc->sc_ndata.modifiers) { DPRINTF(0, "mod: 0x%04x\n", sc->sc_ndata.modifiers); } for (i = 0; i < UKBD_NKEYCODE; i++) { if (sc->sc_ndata.keycode[i]) { DPRINTF(0, "[%d] = %d\n", i, sc->sc_ndata.keycode[i]); } } #endif /* USB_DEBUG */ ukbd_interrupt(sc); } tr_setup: if (sc->sc_flags & UKBD_FLAG_INTR_STALL) { usbd_transfer_start(sc->sc_xfer[1]); return; } if (sc->sc_inputs < UKBD_IN_BUF_FULL) { usbd_start_hardware(xfer); } else { DPRINTF(0, "input queue is full!\n"); } return; tr_error: DPRINTF(0, "error=%s\n", usbd_errstr(xfer->error)); if (xfer->error != USBD_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= UKBD_FLAG_INTR_STALL; usbd_transfer_start(sc->sc_xfer[1]); } return; } static void ukbd_set_leds_callback(struct usbd_xfer *xfer) { usb_device_request_t *req = xfer->buffer; struct ukbd_softc *sc = xfer->priv_sc; USBD_CHECK_STATUS(xfer); tr_transferred: tr_setup: if (sc->sc_flags & UKBD_FLAG_SET_LEDS) { sc->sc_flags &= ~UKBD_FLAG_SET_LEDS; req->bmRequestType = UT_WRITE_CLASS_INTERFACE; req->bRequest = UR_SET_REPORT; USETW2(req->wValue, UHID_OUTPUT_REPORT, 0); USETW(req->wIndex, sc->sc_iface->idesc->bInterfaceNumber); USETW(req->wLength, 1); req->bData[0] = sc->sc_leds; usbd_start_hardware(xfer); } return; tr_error: DPRINTF(0, "error=%s\n", usbd_errstr(xfer->error)); return; } static const struct usbd_config ukbd_config[UKBD_N_TRANSFER] = { [0] = { .type = UE_INTERRUPT, .endpoint = -1, /* any */ .direction = UE_DIR_IN, .flags = USBD_SHORT_XFER_OK, .bufsize = 0, /* use wMaxPacketSize */ .callback = &ukbd_intr_callback, }, [1] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .callback = &ukbd_clear_stall_callback, .timeout = 1000, /* 1 second */ }, [2] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t) + 1, .callback = &ukbd_set_leds_callback, .timeout = 1000, /* 1 second */ }, }; static int ukbd_probe(device_t dev) { keyboard_switch_t *sw = kbd_get_switch(UKBD_DRIVER_NAME); struct usb_attach_arg *uaa = device_get_ivars(dev); usb_interface_descriptor_t *id; DPRINTF(10, "\n"); if (sw == NULL) { return UMATCH_NONE; } if (uaa->iface == NULL) { /* attach to ifaces only */ return UMATCH_NONE; } /* check that the keyboard speaks the boot protocol: */ id = usbd_get_interface_descriptor(uaa->iface); if (id && (id->bInterfaceClass == UICLASS_HID) && (id->bInterfaceSubClass == UISUBCLASS_BOOT) && (id->bInterfaceProtocol == UPROTO_BOOT_KEYBOARD)) { return UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO; } return UMATCH_NONE; } static int ukbd_attach(device_t dev) { struct ukbd_softc *sc = device_get_softc(dev); struct usb_attach_arg *uaa = device_get_ivars(dev); int32_t unit = device_get_unit(dev); keyboard_t *kbd = &(sc->sc_kbd); usbd_status err; u_int16_t n; if (sc == NULL) { return ENOMEM; } mtx_assert(&Giant, MA_OWNED); kbd_init_struct(kbd, UKBD_DRIVER_NAME, KB_OTHER, unit, 0, 0, 0); kbd->kb_data = (void *)sc; usbd_set_desc(dev, uaa->device); sc->sc_udev = uaa->device; sc->sc_iface = uaa->iface; sc->sc_iface_index = uaa->iface_index; sc->sc_mode = K_XLATE; sc->sc_iface = uaa->iface; __callout_init_mtx(&(sc->sc_callout), &Giant, CALLOUT_RETURNUNLOCKED); err = usbd_transfer_setup(uaa->device, uaa->iface_index, sc->sc_xfer, ukbd_config, UKBD_N_TRANSFER, sc, &Giant); if (err) { DPRINTF(0, "error=%s\n", usbd_errstr(err)) ; goto detach; } /* setup default keyboard maps */ sc->sc_keymap = key_map; sc->sc_accmap = accent_map; for (n = 0; n < UKBD_NFKEY; n++) { sc->sc_fkeymap[n] = fkey_tab[n]; } kbd_set_maps(kbd, &(sc->sc_keymap), &(sc->sc_accmap), sc->sc_fkeymap, UKBD_NFKEY); KBD_FOUND_DEVICE(kbd); ukbd_clear_state(kbd); /* * FIXME: set the initial value for lock keys in "sc_state" * according to the BIOS data? */ KBD_PROBE_DONE(kbd); if ((usbd_get_quirks(uaa->device)->uq_flags & UQ_NO_SET_PROTO) == 0) { err = usbreq_set_protocol(sc->sc_udev, sc->sc_iface_index, 0); DPRINTF(5, "protocol set\n"); if (err) { device_printf(dev, "set protocol failed\n"); goto detach; } } /* ignore if SETIDLE fails, hence it is not crucial */ err = usbreq_set_idle(sc->sc_udev, sc->sc_iface_index, 0, 0); ukbd_ioctl(kbd, KDSETLED, (caddr_t)&(sc->sc_state)); KBD_INIT_DONE(kbd); if (kbd_register(kbd) < 0) { goto detach; } KBD_CONFIG_DONE(kbd); ukbd_enable(kbd); #ifdef KBD_INSTALL_CDEV if (kbd_attach(kbd)) { goto detach; } #endif sc->sc_flags |= UKBD_FLAG_ATTACHED; if (bootverbose) { genkbd_diag(kbd, bootverbose); } /* start the keyboard */ usbd_transfer_start(sc->sc_xfer[0]); /* start the timer */ mtx_lock(&Giant); ukbd_timeout(sc); return 0; /* success */ detach: ukbd_detach(dev); return ENXIO; /* error */ } int ukbd_detach(device_t dev) { struct ukbd_softc *sc = device_get_softc(dev); int error; mtx_assert(&Giant, MA_OWNED); DPRINTF(0, "\n"); if (sc->sc_flags & UKBD_FLAG_POLLING) { panic("cannot detach polled keyboard!\n"); } sc->sc_flags |= UKBD_FLAG_GONE; __callout_stop(&(sc->sc_callout)); ukbd_disable(&(sc->sc_kbd)); #ifdef KBD_INSTALL_CDEV if (sc->sc_flags & UKBD_FLAG_ATTACHED) { error = kbd_detach(&(sc->sc_kbd)); if (error) { /* usb attach cannot return an error */ device_printf(dev, "WARNING: kbd_detach() " "returned non-zero! (ignored)\n"); } } #endif if (KBD_IS_CONFIGURED(&(sc->sc_kbd))) { error = kbd_unregister(&(sc->sc_kbd)); if (error) { /* usb attach cannot return an error */ device_printf(dev, "WARNING: kbd_unregister() " "returned non-zero! (ignored)\n"); } } sc->sc_kbd.kb_flags = 0; usbd_transfer_unsetup(sc->sc_xfer, UKBD_N_TRANSFER); __callout_drain(&(sc->sc_callout)); DPRINTF(0, "%s: disconnected\n", device_get_nameunit(dev)); return 0; } static int ukbd_resume(device_t dev) { struct ukbd_softc *sc = device_get_softc(dev); mtx_assert(&Giant, MA_OWNED); ukbd_clear_state(&(sc->sc_kbd)); return 0; } /* early keyboard probe, not supported */ static int ukbd_configure(int flags) { return 0; } /* detect a keyboard, not used */ static int ukbd__probe(int unit, void *arg, int flags) { mtx_assert(&Giant, MA_OWNED); return ENXIO; } /* reset and initialize the device, not used */ static int ukbd_init(int unit, keyboard_t **kbdp, void *arg, int flags) { mtx_assert(&Giant, MA_OWNED); return ENXIO; } /* test the interface to the device, not used */ static int ukbd_test_if(keyboard_t *kbd) { mtx_assert(&Giant, MA_OWNED); return 0; } /* finish using this keyboard, not used */ static int ukbd_term(keyboard_t *kbd) { mtx_assert(&Giant, MA_OWNED); return ENXIO; } /* keyboard interrupt routine, not used */ static int ukbd_intr(keyboard_t *kbd, void *arg) { mtx_assert(&Giant, MA_OWNED); return 0; } /* lock the access to the keyboard, not used */ static int ukbd_lock(keyboard_t *kbd, int lock) { mtx_assert(&Giant, MA_OWNED); return 1; } /* * Enable the access to the device; until this function is called, * the client cannot read from the keyboard. */ static int ukbd_enable(keyboard_t *kbd) { mtx_assert(&Giant, MA_OWNED); KBD_ACTIVATE(kbd); return 0; } /* disallow the access to the device */ static int ukbd_disable(keyboard_t *kbd) { mtx_assert(&Giant, MA_OWNED); KBD_DEACTIVATE(kbd); return 0; } /* check if data is waiting */ static int ukbd_check(keyboard_t *kbd) { struct ukbd_softc *sc = kbd->kb_data; if (!mtx_owned(&Giant)) { return 0; /* XXX */ } mtx_assert(&Giant, MA_OWNED); if (!KBD_IS_ACTIVE(kbd)) { return 0; } #ifdef UKBD_EMULATE_ATSCANCODE if (sc->sc_buffered_char[0]) { return 1; } #endif if (sc->sc_inputs > 0) { return 1; } return 0; } /* check if char is waiting */ static int ukbd_check_char(keyboard_t *kbd) { struct ukbd_softc *sc = kbd->kb_data; if (!mtx_owned(&Giant)) { return 0; /* XXX */ } mtx_assert(&Giant, MA_OWNED); if (!KBD_IS_ACTIVE(kbd)) { return 0; } if ((sc->sc_composed_char > 0) && (!(sc->sc_flags & UKBD_FLAG_COMPOSE))) { return 1; } return ukbd_check(kbd); } /* read one byte from the keyboard if it's allowed */ static int ukbd_read(keyboard_t *kbd, int wait) { struct ukbd_softc *sc = kbd->kb_data; int32_t usbcode; #ifdef UKBD_EMULATE_ATSCANCODE u_int32_t keycode; u_int32_t scancode; #endif if (!mtx_owned(&Giant)) { return -1; /* XXX */ } mtx_assert(&Giant, MA_OWNED); #ifdef UKBD_EMULATE_ATSCANCODE if (sc->sc_buffered_char[0]) { scancode = sc->sc_buffered_char[0]; if (scancode & SCAN_PREFIX) { sc->sc_buffered_char[0] &= ~SCAN_PREFIX; return ((scancode & SCAN_PREFIX_E0) ? 0xe0 : 0xe1); } sc->sc_buffered_char[0] = sc->sc_buffered_char[1]; sc->sc_buffered_char[1] = 0; return scancode; } #endif /* UKBD_EMULATE_ATSCANCODE */ /* XXX */ usbcode = ukbd_get_key(sc); if (!KBD_IS_ACTIVE(kbd) || (usbcode == -1)) { return -1; } ++(kbd->kb_count); #ifdef UKBD_EMULATE_ATSCANCODE keycode = ukbd_trtab[KEY_INDEX(usbcode)]; if (keycode == NN) { return -1; } return ukbd_key2scan(sc, keycode, sc->sc_ndata.modifiers, (usbcode & KEY_RELEASE)); #else /* !UKBD_EMULATE_ATSCANCODE */ return usbcode; #endif /* UKBD_EMULATE_ATSCANCODE */ } /* read char from the keyboard */ static u_int32_t ukbd_read_char(keyboard_t *kbd, int wait) { struct ukbd_softc *sc = kbd->kb_data; u_int32_t action; u_int32_t keycode; int32_t usbcode; #ifdef UKBD_EMULATE_ATSCANCODE u_int32_t scancode; #endif if (!mtx_owned(&Giant)) { return NOKEY; /* XXX */ } mtx_assert(&Giant, MA_OWNED); next_code: /* do we have a composed char to return ? */ if ((sc->sc_composed_char > 0) && (!(sc->sc_flags & UKBD_FLAG_COMPOSE))) { action = sc->sc_composed_char; sc->sc_composed_char = 0; if (action > 0xFF) { goto errkey; } goto done; } #ifdef UKBD_EMULATE_ATSCANCODE /* do we have a pending raw scan code? */ if (sc->sc_mode == K_RAW) { scancode = sc->sc_buffered_char[0]; if (scancode) { if (scancode & SCAN_PREFIX) { sc->sc_buffered_char[0] = (scancode & ~SCAN_PREFIX); return ((scancode & SCAN_PREFIX_E0) ? 0xe0 : 0xe1); } sc->sc_buffered_char[0] = sc->sc_buffered_char[1]; sc->sc_buffered_char[1] = 0; return scancode; } } #endif /* UKBD_EMULATE_ATSCANCODE */ /* see if there is something in the keyboard port */ /* XXX */ usbcode = ukbd_get_key(sc); if (usbcode == -1) { return NOKEY; } ++kbd->kb_count; #ifdef UKBD_EMULATE_ATSCANCODE /* USB key index -> key code -> AT scan code */ keycode = ukbd_trtab[KEY_INDEX(usbcode)]; if (keycode == NN) { return NOKEY; } /* return an AT scan code for the K_RAW mode */ if (sc->sc_mode == K_RAW) { return ukbd_key2scan(sc, keycode, sc->sc_ndata.modifiers, (usbcode & KEY_RELEASE)); } #else /* !UKBD_EMULATE_ATSCANCODE */ /* return the byte as is for the K_RAW mode */ if (sc->sc_mode == K_RAW) { return usbcode; } /* USB key index -> key code */ keycode = ukbd_trtab[KEY_INDEX(usbcode)]; if (keycode == NN) { return NOKEY; } #endif /* UKBD_EMULATE_ATSCANCODE */ switch (keycode) { case 0x38: /* left alt (compose key) */ if (usbcode & KEY_RELEASE) { if (sc->sc_flags & UKBD_FLAG_COMPOSE) { sc->sc_flags &= ~UKBD_FLAG_COMPOSE; if (sc->sc_composed_char > 0xFF) { sc->sc_composed_char = 0; } } } else { if (!(sc->sc_flags & UKBD_FLAG_COMPOSE)) { sc->sc_flags |= UKBD_FLAG_COMPOSE; sc->sc_composed_char = 0; } } break; /* XXX: I don't like these... */ case 0x5c: /* print screen */ if (sc->sc_flags & ALTS) { keycode = 0x54; /* sysrq */ } break; case 0x68: /* pause/break */ if (sc->sc_flags & CTLS) { keycode = 0x6c; /* break */ } break; } /* return the key code in the K_CODE mode */ if (usbcode & KEY_RELEASE) { keycode |= SCAN_RELEASE; } if (sc->sc_mode == K_CODE) { return keycode; } /* compose a character code */ if (sc->sc_flags & UKBD_FLAG_COMPOSE) { switch (keycode) { /* key pressed, process it */ case 0x47: case 0x48: case 0x49: /* keypad 7,8,9 */ sc->sc_composed_char *= 10; sc->sc_composed_char += keycode - 0x40; goto check_composed; case 0x4B: case 0x4C: case 0x4D: /* keypad 4,5,6 */ sc->sc_composed_char *= 10; sc->sc_composed_char += keycode - 0x47; goto check_composed; case 0x4F: case 0x50: case 0x51: /* keypad 1,2,3 */ sc->sc_composed_char *= 10; sc->sc_composed_char += keycode - 0x4E; goto check_composed; case 0x52: /* keypad 0 */ sc->sc_composed_char *= 10; goto check_composed; /* key released, no interest here */ case SCAN_RELEASE | 0x47: case SCAN_RELEASE | 0x48: case SCAN_RELEASE | 0x49: /* keypad 7,8,9 */ case SCAN_RELEASE | 0x4B: case SCAN_RELEASE | 0x4C: case SCAN_RELEASE | 0x4D: /* keypad 4,5,6 */ case SCAN_RELEASE | 0x4F: case SCAN_RELEASE | 0x50: case SCAN_RELEASE | 0x51: /* keypad 1,2,3 */ case SCAN_RELEASE | 0x52: /* keypad 0 */ goto next_code; case 0x38: /* left alt key */ break; default: if (sc->sc_composed_char > 0) { sc->sc_flags &= ~UKBD_FLAG_COMPOSE; sc->sc_composed_char = 0; goto errkey; } break; } } /* keycode to key action */ action = genkbd_keyaction(kbd, SCAN_CHAR(keycode), (keycode & SCAN_RELEASE), &sc->sc_state, &sc->sc_accents); if (action == NOKEY) { goto next_code; } done: return action; check_composed: if (sc->sc_composed_char <= 0xFF) { goto next_code; } errkey: return ERRKEY; } /* some useful control functions */ static int ukbd_ioctl(keyboard_t *kbd, u_long cmd, caddr_t arg) { /* translate LED_XXX bits into the device specific bits */ static const u_int8_t ledmap[8] = { 0, 2, 1, 3, 4, 6, 5, 7, }; struct ukbd_softc *sc = kbd->kb_data; int i; if (!mtx_owned(&Giant)) { /* XXX big problem: * If scroll lock is pressed and * "printf()" is called, the CPU will * get here, to un-scroll lock the * keyboard. But if "printf()" acquires * the "Giant" lock, there will be a * locking order reversal problem, * so the keyboard system must get * out of "Giant" first, before * the CPU can proceed here ... */ return EINVAL; } mtx_assert(&Giant, MA_OWNED); switch (cmd) { case KDGKBMODE: /* get keyboard mode */ *(int *)arg = sc->sc_mode; break; case KDSKBMODE: /* set keyboard mode */ switch (*(int *)arg) { case K_XLATE: if (sc->sc_mode != K_XLATE) { /* make lock key state and LED state match */ sc->sc_state &= ~LOCK_MASK; sc->sc_state |= KBD_LED_VAL(kbd); } /* FALLTHROUGH */ case K_RAW: case K_CODE: if (sc->sc_mode != *(int *)arg) { ukbd_clear_state(kbd); sc->sc_mode = *(int *)arg; } break; default: return EINVAL; } break; case KDGETLED: /* get keyboard LED */ *(int *)arg = KBD_LED_VAL(kbd); break; case KDSETLED: /* set keyboard LED */ /* NOTE: lock key state in "sc_state" won't be changed */ if (*(int *)arg & ~LOCK_MASK) { return EINVAL; } i = *(int *)arg; /* replace CAPS LED with ALTGR LED for ALTGR keyboards */ if (kbd->kb_keymap->n_keys > ALTGR_OFFSET) { if (i & ALKED) i |= CLKED; else i &= ~CLKED; } if (KBD_HAS_DEVICE(kbd)) { ukbd_set_leds(sc, ledmap[i & LED_MASK]); } KBD_LED_VAL(kbd) = *(int *)arg; break; case KDGKBSTATE: /* get lock key state */ *(int *)arg = sc->sc_state & LOCK_MASK; break; case KDSKBSTATE: /* set lock key state */ if (*(int *)arg & ~LOCK_MASK) { return EINVAL; } sc->sc_state &= ~LOCK_MASK; sc->sc_state |= *(int *)arg; /* set LEDs and quit */ return ukbd_ioctl(kbd, KDSETLED, arg); case KDSETREPEAT: /* set keyboard repeat rate (new interface) */ if (!KBD_HAS_DEVICE(kbd)) { return 0; } if (((int *)arg)[1] < 0) { return EINVAL; } if (((int *)arg)[0] < 0) { return EINVAL; } if (((int *)arg)[0] < 200) /* fastest possible value */ kbd->kb_delay1 = 200; else kbd->kb_delay1 = ((int *)arg)[0]; kbd->kb_delay2 = ((int *)arg)[1]; return 0; case KDSETRAD: /* set keyboard repeat rate (old interface) */ return ukbd_set_typematic(kbd, *(int *)arg); case PIO_KEYMAP: /* set keyboard translation table */ case PIO_KEYMAPENT: /* set keyboard translation table entry */ case PIO_DEADKEYMAP: /* set accent key translation table */ sc->sc_accents = 0; /* FALLTHROUGH */ default: return genkbd_commonioctl(kbd, cmd, arg); #ifdef USB_DEBUG case USB_SETDEBUG: ukbd_debug = *(int *)arg; break; #endif } return 0; } /* clear the internal state of the keyboard */ static void ukbd_clear_state(keyboard_t *kbd) { struct ukbd_softc *sc = kbd->kb_data; if (!mtx_owned(&Giant)) { return; /* XXX */ } mtx_assert(&Giant, MA_OWNED); sc->sc_flags &= ~(UKBD_FLAG_COMPOSE|UKBD_FLAG_POLLING); sc->sc_state &= LOCK_MASK; /* preserve locking key state */ sc->sc_accents = 0; sc->sc_composed_char = 0; #ifdef UKBD_EMULATE_ATSCANCODE sc->sc_buffered_char[0] = 0; sc->sc_buffered_char[1] = 0; #endif bzero(&sc->sc_ndata, sizeof(sc->sc_ndata)); bzero(&sc->sc_odata, sizeof(sc->sc_odata)); bzero(&sc->sc_ntime, sizeof(sc->sc_ntime)); bzero(&sc->sc_otime, sizeof(sc->sc_otime)); return; } /* save the internal state, not used */ static int ukbd_get_state(keyboard_t *kbd, void *buf, size_t len) { mtx_assert(&Giant, MA_OWNED); return (len == 0) ? 1 : -1; } /* set the internal state, not used */ static int ukbd_set_state(keyboard_t *kbd, void *buf, size_t len) { mtx_assert(&Giant, MA_OWNED); return EINVAL; } static int ukbd_poll(keyboard_t *kbd, int on) { struct ukbd_softc *sc = kbd->kb_data; if (!mtx_owned(&Giant)) { return 0; /* XXX */ } mtx_assert(&Giant, MA_OWNED); if (on) { sc->sc_flags |= UKBD_FLAG_POLLING; } else { sc->sc_flags &= ~UKBD_FLAG_POLLING; } return 0; } /* local functions */ static void ukbd_set_leds(struct ukbd_softc *sc, u_int8_t leds) { DPRINTF(0, "leds=0x%02x\n", leds); sc->sc_leds = leds; sc->sc_flags |= UKBD_FLAG_SET_LEDS; /* start transfer, if not already started */ usbd_transfer_start(sc->sc_xfer[2]); return; } static int ukbd_set_typematic(keyboard_t *kbd, int code) { static const int delays[] = { 250, 500, 750, 1000 }; static const int rates[] = { 34, 38, 42, 46, 50, 55, 59, 63, 68, 76, 84, 92, 100, 110, 118, 126, 136, 152, 168, 184, 200, 220, 236, 252, 272, 304, 336, 368, 400, 440, 472, 504 }; if (code & ~0x7f) { return EINVAL; } kbd->kb_delay1 = delays[(code >> 5) & 3]; kbd->kb_delay2 = rates[code & 0x1f]; return 0; } #ifdef UKBD_EMULATE_ATSCANCODE static int ukbd_key2scan(struct ukbd_softc *sc, int code, int shift, int up) { static const int scan[] = { 0x1c, 0x1d, 0x35, 0x37 | SCAN_PREFIX_SHIFT, /* PrintScreen */ 0x38, 0x47, 0x48, 0x49, 0x4b, 0x4d, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x46, /* XXX Pause/Break */ 0x5b, 0x5c, 0x5d, /* SUN TYPE 6 USB KEYBOARD */ 0x68, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x25, 0x1f, 0x1e, 0x20, }; if ((code >= 89) && (code < (89 + (sizeof(scan)/sizeof(scan[0]))))) { code = scan[code - 89] | SCAN_PREFIX_E0; } /* Pause/Break */ if ((code == 104) && (!(shift & (MOD_CONTROL_L | MOD_CONTROL_R)))) { code = (0x45 | SCAN_PREFIX_E1 | SCAN_PREFIX_CTL); } if (shift & (MOD_SHIFT_L | MOD_SHIFT_R)) { code &= ~SCAN_PREFIX_SHIFT; } code |= (up ? SCAN_RELEASE : SCAN_PRESS); if (code & SCAN_PREFIX) { if (code & SCAN_PREFIX_CTL) { /* Ctrl */ sc->sc_buffered_char[0] = (0x1d | (code & SCAN_RELEASE)); sc->sc_buffered_char[1] = (code & ~SCAN_PREFIX); } else if (code & SCAN_PREFIX_SHIFT) { /* Shift */ sc->sc_buffered_char[0] = (0x2a | (code & SCAN_RELEASE)); sc->sc_buffered_char[1] = (code & ~SCAN_PREFIX_SHIFT); } else { sc->sc_buffered_char[0] = (code & ~SCAN_PREFIX); sc->sc_buffered_char[1] = 0; } return ((code & SCAN_PREFIX_E0) ? 0xe0 : 0xe1); } return code; } #endif /* UKBD_EMULATE_ATSCANCODE */ keyboard_switch_t ukbdsw = { .probe = &ukbd__probe, .init = &ukbd_init, .term = &ukbd_term, .intr = &ukbd_intr, .test_if = &ukbd_test_if, .enable = &ukbd_enable, .disable = &ukbd_disable, .read = &ukbd_read, .check = &ukbd_check, .read_char = &ukbd_read_char, .check_char = &ukbd_check_char, .ioctl = &ukbd_ioctl, .lock = &ukbd_lock, .clear_state = &ukbd_clear_state, .get_state = &ukbd_get_state, .set_state = &ukbd_set_state, .get_fkeystr = &genkbd_get_fkeystr, .poll = &ukbd_poll, .diag = &genkbd_diag, }; KEYBOARD_DRIVER(ukbd, ukbdsw, ukbd_configure); static int ukbd_driver_load(module_t mod, int what, void *arg) { switch (what) { case MOD_LOAD: kbd_add_driver(&ukbd_kbd_driver); break; case MOD_UNLOAD: kbd_delete_driver(&ukbd_kbd_driver); break; } return usbd_driver_load(mod, what, 0); } static devclass_t ukbd_devclass; static device_method_t ukbd_methods[] = { DEVMETHOD(device_probe, ukbd_probe), DEVMETHOD(device_attach, ukbd_attach), DEVMETHOD(device_detach, ukbd_detach), DEVMETHOD(device_resume, ukbd_resume), { 0, 0 } }; static driver_t ukbd_driver = { .name = "ukbd", .methods = ukbd_methods, .size = sizeof(struct ukbd_softc), }; DRIVER_MODULE(ukbd, uhub, ukbd_driver, ukbd_devclass, ukbd_driver_load, 0); MODULE_DEPEND(ukbd, usb, 1, 1, 1); pwcbsd/usb/ulpt.c000644 000423 000000 00000044604 10551670754 014541 0ustar00luigiwheel000000 000000 /* $NetBSD: ulpt.c,v 1.60 2003/10/04 21:19:50 augustss Exp $ */ /*- * Copyright (c) 1998, 2003 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * Printer Class spec: http://www.usb.org/developers/data/devclass/usbprint109.PDF * Printer Class spec: http://www.usb.org/developers/devclass_docs/usbprint11.pdf */ #include #include #include #include #include #include #include #include #include #include __FBSDID("$FreeBSD: src/sys/dev/usb/ulpt.c $"); #ifdef USB_DEBUG #define DPRINTF(n,fmt,...) \ do { if (ulpt_debug > (n)) { \ printf("%s: " fmt, __FUNCTION__,## __VA_ARGS__); } } while (0) static int ulpt_debug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, ulpt, CTLFLAG_RW, 0, "USB ulpt"); SYSCTL_INT(_hw_usb_ulpt, OID_AUTO, debug, CTLFLAG_RW, &ulpt_debug, 0, "ulpt debug level"); #else #define DPRINTF(...) #endif #define ULPT_BSIZE (1<<17) /* bytes */ #define ULPT_IFQ_MAXLEN 2 /* units */ #define ULPT_WATCHDOG_INTERVAL 5 /* times per second */ #define ULPT_N_TRANSFER 6 /* units */ #define UR_GET_DEVICE_ID 0x00 #define UR_GET_PORT_STATUS 0x01 #define UR_SOFT_RESET 0x02 #define LPS_NERR 0x08 /* printer no error */ #define LPS_SELECT 0x10 /* printer selected */ #define LPS_NOPAPER 0x20 /* printer out of paper */ #define LPS_INVERT (LPS_SELECT|LPS_NERR) #define LPS_MASK (LPS_SELECT|LPS_NERR|LPS_NOPAPER) struct ulpt_softc { struct usb_cdev sc_cdev; struct __callout sc_watchdog; struct mtx sc_mtx; device_t sc_dev; struct usbd_xfer * sc_xfer[ULPT_N_TRANSFER]; u_int8_t sc_flags; #define ULPT_FLAG_NO_READ 0x01 /* device has no read endpoint */ #define ULPT_FLAG_DUMP_READ 0x02 /* device is not opened for read */ #define ULPT_FLAG_READ_STALL 0x04 /* read transfer stalled */ #define ULPT_FLAG_WRITE_STALL 0x08 /* write transfer stalled */ #define ULPT_FLAG_RESETTING 0x10 /* device is resetting */ u_int8_t sc_iface_no; u_int8_t sc_last_status; }; static void ulpt_watchdog(void *__sc) { struct ulpt_softc *sc = __sc; mtx_assert(&(sc->sc_mtx), MA_OWNED); DPRINTF(2, "start sc=%p\n", sc); /* start reading of status, if not already started */ usbd_transfer_start(sc->sc_xfer[2]); if ((sc->sc_flags & (ULPT_FLAG_NO_READ| ULPT_FLAG_DUMP_READ)) && (!(sc->sc_flags & ULPT_FLAG_RESETTING)) && (sc->sc_cdev.sc_flags & (USB_CDEV_FLAG_OPEN_READ| USB_CDEV_FLAG_OPEN_WRITE)) && (!(sc->sc_cdev.sc_flags & USB_CDEV_FLAG_CLOSING_READ))) { /* start reading of data, if not already started */ usbd_transfer_start(sc->sc_xfer[1]); } __callout_reset(&(sc->sc_watchdog), hz / ULPT_WATCHDOG_INTERVAL, &ulpt_watchdog, sc); mtx_unlock(&(sc->sc_mtx)); return; } static void ulpt_write_callback(struct usbd_xfer *xfer) { struct ulpt_softc *sc = xfer->priv_sc; u_int32_t actlen; USBD_CHECK_STATUS(xfer); tr_transferred: tr_setup: if (sc->sc_flags & ULPT_FLAG_WRITE_STALL) { usbd_transfer_start(sc->sc_xfer[4]); return; } if (usb_cdev_get_data(&(sc->sc_cdev), xfer->buffer, ULPT_BSIZE, &actlen, 0)) { xfer->length = actlen; usbd_start_hardware(xfer); } return; tr_error: if (xfer->error != USBD_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= ULPT_FLAG_WRITE_STALL; usbd_transfer_start(sc->sc_xfer[4]); } return; } static void ulpt_write_clear_stall_callback(struct usbd_xfer *xfer) { struct ulpt_softc *sc = xfer->priv_sc; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, sc->sc_xfer[0]); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, sc->sc_xfer[0]); sc->sc_flags &= ~ULPT_FLAG_WRITE_STALL; usbd_transfer_start(sc->sc_xfer[0]); return; tr_error: /* bomb out */ sc->sc_flags &= ~ULPT_FLAG_WRITE_STALL; usb_cdev_get_data_error(&(sc->sc_cdev)); return; } static void ulpt_read_callback(struct usbd_xfer *xfer) { struct ulpt_softc *sc = xfer->priv_sc; struct usbd_mbuf *m; USBD_CHECK_STATUS(xfer); tr_transferred: if (sc->sc_flags & (ULPT_FLAG_NO_READ|ULPT_FLAG_DUMP_READ)) { return; } usb_cdev_put_data(&(sc->sc_cdev), xfer->buffer, xfer->actlen, 1); tr_setup: if (sc->sc_flags & ULPT_FLAG_READ_STALL) { usbd_transfer_start(sc->sc_xfer[5]); return; } USBD_IF_POLL(&sc->sc_cdev.sc_rdq_free, m); if (m) { usbd_start_hardware(xfer); } return; tr_error: if (xfer->error != USBD_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= ULPT_FLAG_READ_STALL; usbd_transfer_start(sc->sc_xfer[5]); } return; } static void ulpt_read_clear_stall_callback(struct usbd_xfer *xfer) { struct ulpt_softc *sc = xfer->priv_sc; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, sc->sc_xfer[1]); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, sc->sc_xfer[1]); sc->sc_flags &= ~ULPT_FLAG_READ_STALL; usbd_transfer_start(sc->sc_xfer[1]); return; tr_error: /* bomb out */ sc->sc_flags &= ~ULPT_FLAG_READ_STALL; usb_cdev_put_data_error(&(sc->sc_cdev)); return; } static void ulpt_status_callback(struct usbd_xfer *xfer) { struct ulpt_softc *sc = xfer->priv_sc; usb_device_request_t *req = xfer->buffer; u_int8_t cur_status = req->bData[0]; u_int8_t new_status; USBD_CHECK_STATUS(xfer); tr_transferred: cur_status = (cur_status ^ LPS_INVERT) & LPS_MASK; new_status = cur_status & ~sc->sc_last_status; sc->sc_last_status = cur_status; if (new_status & LPS_SELECT) log(LOG_NOTICE, "%s: offline\n", device_get_nameunit(sc->sc_dev)); else if (new_status & LPS_NOPAPER) log(LOG_NOTICE, "%s: out of paper\n", device_get_nameunit(sc->sc_dev)); else if (new_status & LPS_NERR) log(LOG_NOTICE, "%s: output error\n", device_get_nameunit(sc->sc_dev)); return; tr_setup: req->bmRequestType = UT_READ_CLASS_INTERFACE; req->bRequest = UR_GET_PORT_STATUS; USETW(req->wValue, 0); USETW(req->wIndex, sc->sc_iface_no); USETW(req->wLength, 1); usbd_start_hardware(xfer); return; tr_error: DPRINTF(0, "error=%s\n", usbd_errstr(xfer->error)); return; } static void ulpt_reset_callback(struct usbd_xfer *xfer) { struct ulpt_softc *sc = xfer->priv_sc; usb_device_request_t *req = xfer->buffer; USBD_CHECK_STATUS(xfer); tr_error: if (xfer->error == USBD_CANCELLED) { return; } if (req->bmRequestType == UT_WRITE_CLASS_OTHER) { /* * There was a mistake in the USB printer 1.0 spec that * gave the request type as UT_WRITE_CLASS_OTHER; it * should have been UT_WRITE_CLASS_INTERFACE. Many * printers use the old one, so try both: */ req->bmRequestType = UT_WRITE_CLASS_INTERFACE; /* 1.1 */ req->bRequest = UR_SOFT_RESET; USETW(req->wValue, 0); USETW(req->wIndex, sc->sc_iface_no); USETW(req->wLength, 0); usbd_start_hardware(xfer); return; } tr_transferred: usb_cdev_wakeup(&(sc->sc_cdev)); return; tr_setup: req->bmRequestType = UT_WRITE_CLASS_OTHER; /* 1.0 */ req->bRequest = UR_SOFT_RESET; USETW(req->wValue, 0); USETW(req->wIndex, sc->sc_iface_no); USETW(req->wLength, 0); usbd_start_hardware(xfer); return; } static const struct usbd_config ulpt_config[ULPT_N_TRANSFER] = { [0] = { .type = UE_BULK, .endpoint = -1, /* any */ .direction = UE_DIR_OUT, .bufsize = ULPT_BSIZE, .flags = 0, .callback = &ulpt_write_callback, }, [1] = { .type = UE_BULK, .endpoint = -1, /* any */ .direction = UE_DIR_IN, .bufsize = ULPT_BSIZE, .flags = USBD_SHORT_XFER_OK, .callback = &ulpt_read_callback, }, [2] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t) + 1, .callback = &ulpt_status_callback, .timeout = 1000, /* 1 second */ }, [3] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .callback = &ulpt_reset_callback, .timeout = 1000, /* 1 second */ }, [4] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .callback = &ulpt_write_clear_stall_callback, .timeout = 1000, /* 1 second */ }, [5] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .callback = &ulpt_read_clear_stall_callback, .timeout = 1000, /* 1 second */ }, }; static void ulpt_start_read(struct usb_cdev *cdev) { struct ulpt_softc *sc = cdev->sc_priv_ptr; usbd_transfer_start(sc->sc_xfer[1]); return; } static void ulpt_stop_read(struct usb_cdev *cdev) { struct ulpt_softc *sc = cdev->sc_priv_ptr; usbd_transfer_stop(sc->sc_xfer[5]); usbd_transfer_stop(sc->sc_xfer[1]); return; } static void ulpt_start_write(struct usb_cdev *cdev) { struct ulpt_softc *sc = cdev->sc_priv_ptr; usbd_transfer_start(sc->sc_xfer[0]); return; } static void ulpt_stop_write(struct usb_cdev *cdev) { struct ulpt_softc *sc = cdev->sc_priv_ptr; usbd_transfer_stop(sc->sc_xfer[4]); usbd_transfer_stop(sc->sc_xfer[0]); return; } static int32_t ulpt_open(struct usb_cdev *cdev, int32_t fflags, int32_t devtype, struct thread *td) { u_int8_t prime = ((cdev->sc_last_cdev == cdev->sc_cdev[0]) && (cdev->sc_first_open)); struct ulpt_softc *sc = cdev->sc_priv_ptr; int32_t error = 0; if (fflags & FREAD) { /* clear stall first */ sc->sc_flags |= ULPT_FLAG_READ_STALL; } if (fflags & FWRITE) { /* clear stall first */ sc->sc_flags |= ULPT_FLAG_WRITE_STALL; } if (prime) { DPRINTF(0, "opening prime device (reset)\n"); sc->sc_flags |= ULPT_FLAG_RESETTING; usbd_transfer_start(sc->sc_xfer[3]); error = usb_cdev_sleep(&(sc->sc_cdev), fflags, 0); usbd_transfer_stop(sc->sc_xfer[3]); sc->sc_flags &= ~ULPT_FLAG_RESETTING; if (error) { goto done; } } if (cdev->sc_flags & USB_CDEV_FLAG_OPEN_READ) { sc->sc_flags &= ~ULPT_FLAG_DUMP_READ; } else { sc->sc_flags |= ULPT_FLAG_DUMP_READ; } done: return error; } static int32_t ulpt_ioctl(struct usb_cdev *cdev, u_long cmd, caddr_t data, int32_t fflags, struct thread *td) { return ENODEV; } /* prototypes */ static device_probe_t ulpt_probe; static device_attach_t ulpt_attach; static device_detach_t ulpt_detach; static int ulpt_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); usb_interface_descriptor_t *id; DPRINTF(10, "\n"); if (uaa->iface == NULL) { return UMATCH_NONE; } id = usbd_get_interface_descriptor(uaa->iface); if ((id != NULL) && (id->bInterfaceClass == UICLASS_PRINTER) && (id->bInterfaceSubClass == UISUBCLASS_PRINTER) && ((id->bInterfaceProtocol == UIPROTO_PRINTER_UNI) || (id->bInterfaceProtocol == UIPROTO_PRINTER_BI) || (id->bInterfaceProtocol == UIPROTO_PRINTER_1284))) { return UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO; } return UMATCH_NONE; } static int ulpt_attach(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct ulpt_softc *sc = device_get_softc(dev); struct usbd_interface *iface_ptr = uaa->iface; usb_interface_descriptor_t *id; const char * p_buf[3]; int32_t iface_index = uaa->iface_index; int32_t iface_alt_index = 0; int32_t unit = device_get_unit(dev); int32_t error; char buf_1[16]; char buf_2[16]; DPRINTF(10, "sc=%p\n", sc); sc->sc_dev = dev; usbd_set_desc(dev, uaa->device); mtx_init(&(sc->sc_mtx), "ulpt lock", NULL, MTX_DEF|MTX_RECURSE); __callout_init_mtx(&(sc->sc_watchdog), &(sc->sc_mtx), CALLOUT_RETURNUNLOCKED); /* search through all the descriptors looking for bidir mode */ while(iface_alt_index < 32) { error = usbd_fill_iface_data (uaa->device, iface_index, iface_alt_index); if (error) { DPRINTF(0, "end of alternate settings, " "error=%s\n", usbd_errstr(error)); goto detach; } id = usbd_get_interface_descriptor(iface_ptr); if ((id->bInterfaceClass == UICLASS_PRINTER) && (id->bInterfaceSubClass == UISUBCLASS_PRINTER) && (id->bInterfaceProtocol == UIPROTO_PRINTER_BI)) { goto found; } iface_alt_index++; } goto detach; found: DPRINTF(0, "setting alternate " "config number: %d\n", iface_alt_index); if (iface_alt_index) { error = usbreq_set_interface (uaa->device, iface_index, iface_alt_index); if (error) { DPRINTF(0, "could not set alternate " "config, error=%s\n", usbd_errstr(error)); goto detach; } } sc->sc_iface_no = id->bInterfaceNumber; error = usbd_transfer_setup(uaa->device, iface_index, sc->sc_xfer, ulpt_config, ULPT_N_TRANSFER, sc, &(sc->sc_mtx)); if (error) { DPRINTF(0, "error=%s\n", usbd_errstr(error)) ; goto detach; } if (usbd_get_quirks(uaa->device)->uq_flags & UQ_BROKEN_BIDIR) { /* this device doesn't handle reading properly. */ sc->sc_flags |= ULPT_FLAG_NO_READ; } device_printf(sc->sc_dev, "using %s-directional mode\n", (sc->sc_flags & ULPT_FLAG_NO_READ) ? "uni" : "bi"); #if 0 /* * This code is disabled because for some mysterious reason it causes * printing not to work. But only sometimes, and mostly with * UHCI and less often with OHCI. *sigh* */ { usb_config_descriptor_t *cd = usbd_get_config_descriptor(dev); usb_device_request_t req; int len, alen; req.bmRequestType = UT_READ_CLASS_INTERFACE; req.bRequest = UR_GET_DEVICE_ID; USETW(req.wValue, cd->bConfigurationValue); USETW2(req.wIndex, id->bInterfaceNumber, id->bAlternateSetting); USETW(req.wLength, sizeof devinfo - 1); error = usbd_do_request_flags(dev, &req, devinfo, USBD_SHORT_XFER_OK, &alen, USBD_DEFAULT_TIMEOUT); if (error) { device_printf(sc->sc_dev, "cannot get device id\n"); } else if (alen <= 2) { device_printf(sc->sc_dev, "empty device id, no " "printer connected?\n"); } else { /* devinfo now contains an IEEE-1284 device ID */ len = ((devinfo[0] & 0xff) << 8) | (devinfo[1] & 0xff); if (len > sizeof devinfo - 3) len = sizeof devinfo - 3; devinfo[len] = 0; printf("%s: device id <", device_get_nameunit(sc->sc_dev)); ieee1284_print_id(devinfo+2); printf(">\n"); } } #endif snprintf(buf_1, sizeof(buf_1), "ulpt%d", unit); snprintf(buf_2, sizeof(buf_2), "unlpt%d", unit); p_buf[0] = buf_1; p_buf[1] = buf_2; p_buf[2] = NULL; sc->sc_cdev.sc_start_read = &ulpt_start_read; sc->sc_cdev.sc_start_write = &ulpt_start_write; sc->sc_cdev.sc_stop_read = &ulpt_stop_read; sc->sc_cdev.sc_stop_write = &ulpt_stop_write; sc->sc_cdev.sc_open = &ulpt_open; sc->sc_cdev.sc_ioctl = &ulpt_ioctl; sc->sc_cdev.sc_flags |= (USB_CDEV_FLAG_FWD_SHORT| USB_CDEV_FLAG_WAKEUP_RD_IMMED| USB_CDEV_FLAG_WAKEUP_WR_IMMED); error = usb_cdev_attach(&(sc->sc_cdev), sc, &(sc->sc_mtx), p_buf, UID_ROOT, GID_OPERATOR, 0644, ULPT_BSIZE, ULPT_IFQ_MAXLEN, ULPT_BSIZE, ULPT_IFQ_MAXLEN); if (error) { goto detach; } /* start watchdog (returns unlocked) */ mtx_lock(&(sc->sc_mtx)); ulpt_watchdog(sc); return 0; detach: ulpt_detach(dev); return ENOMEM; } static int ulpt_detach(device_t dev) { struct ulpt_softc *sc = device_get_softc(dev); DPRINTF(0, "sc=%p\n", sc); usb_cdev_detach(&(sc->sc_cdev)); mtx_lock(&(sc->sc_mtx)); __callout_stop(&(sc->sc_watchdog)); mtx_unlock(&(sc->sc_mtx)); usbd_transfer_unsetup(sc->sc_xfer, ULPT_N_TRANSFER); __callout_drain(&(sc->sc_watchdog)); mtx_destroy(&(sc->sc_mtx)); return 0; } #if 0 /* XXX This does not belong here. */ /* * Print select parts of an IEEE 1284 device ID. */ void ieee1284_print_id(char *str) { char *p, *q; for (p = str-1; p; p = strchr(p, ';')) { p++; /* skip ';' */ if (strncmp(p, "MFG:", 4) == 0 || strncmp(p, "MANUFACTURER:", 14) == 0 || strncmp(p, "MDL:", 4) == 0 || strncmp(p, "MODEL:", 6) == 0) { q = strchr(p, ';'); if (q) printf("%.*s", (int)(q - p + 1), p); } } } #endif static devclass_t ulpt_devclass; static device_method_t ulpt_methods[] = { DEVMETHOD(device_probe, ulpt_probe), DEVMETHOD(device_attach, ulpt_attach), DEVMETHOD(device_detach, ulpt_detach), { 0, 0 } }; static driver_t ulpt_driver = { .name = "ulpt", .methods = ulpt_methods, .size = sizeof(struct ulpt_softc), }; DRIVER_MODULE(ulpt, uhub, ulpt_driver, ulpt_devclass, usbd_driver_load, 0); MODULE_DEPEND(ulpt, usb, 1, 1, 1); pwcbsd/usb/umass.c000644 000423 000000 00000246116 10551670754 014707 0ustar00luigiwheel000000 000000 /*- * Copyright (c) 1999 MAEKAWA Masahide , * Nick Hibma * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD: src/sys/dev/usb/umass.c,v 1.135 2006/03/17 18:16:22 iedowse Exp $ * $NetBSD: umass.c,v 1.28 2000/04/02 23:46:53 augustss Exp $ */ /* Also already merged from NetBSD: * $NetBSD: umass.c,v 1.67 2001/11/25 19:05:22 augustss Exp $ * $NetBSD: umass.c,v 1.90 2002/11/04 19:17:33 pooka Exp $ * $NetBSD: umass.c,v 1.108 2003/11/07 17:03:25 wiz Exp $ * $NetBSD: umass.c,v 1.109 2003/12/04 13:57:31 keihan Exp $ */ /* * Universal Serial Bus Mass Storage Class specs: * http://www.usb.org/developers/devclass_docs/usb_msc_overview_1.2.pdf * http://www.usb.org/developers/devclass_docs/usbmassbulk_10.pdf * http://www.usb.org/developers/devclass_docs/usb_msc_cbi_1.1.pdf * http://www.usb.org/developers/devclass_docs/usbmass-ufi10.pdf */ /* * Ported to NetBSD by Lennart Augustsson . * Parts of the code written by Jason R. Thorpe . */ /* * The driver handles 3 Wire Protocols * - Command/Bulk/Interrupt (CBI) * - Command/Bulk/Interrupt with Command Completion Interrupt (CBI with CCI) * - Mass Storage Bulk-Only (BBB) * (BBB refers Bulk/Bulk/Bulk for Command/Data/Status phases) * * Over these wire protocols it handles the following command protocols * - SCSI * - UFI (floppy command set) * - 8070i (ATAPI) * * UFI and 8070i (ATAPI) are transformed versions of the SCSI command set. The * sc->sc_transform method is used to convert the commands into the appropriate * format (if at all necessary). For example, UFI requires all commands to be * 12 bytes in length amongst other things. * * The source code below is marked and can be split into a number of pieces * (in this order): * * - probe/attach/detach * - generic transfer routines * - BBB * - CBI * - CBI_I (in addition to functions from CBI) * - CAM (Common Access Method) * - SCSI * - UFI * - 8070i (ATAPI) * * The protocols are implemented using a state machine, for the transfers as * well as for the resets. The state machine is contained in umass_t_*_callback. * The state machine is started through either umass_command_start() or * umass_reset(). * * The reason for doing this is a) CAM performs a lot better this way and b) it * avoids using tsleep from interrupt context (for example after a failed * transfer). */ /* * The SCSI related part of this driver has been derived from the * dev/ppbus/vpo.c driver, by Nicolas Souchu (nsouch@freebsd.org). * * The CAM layer uses so called actions which are messages sent to the host * adapter for completion. The actions come in through umass_cam_action. The * appropriate block of routines is called depending on the transport protocol * in use. When the transfer has finished, these routines call * umass_cam_cb again to complete the CAM command. */ #include #include #include #include #include #include #include #include "usbdevs.h" #include #include #include #include #include #include #include #ifdef USB_DEBUG #define DIF(m, x) \ do { \ if (umass_debug & (m)) { x } \ } while (0) #define DPRINTF(sc, m, fmt, ...) \ do { \ if (umass_debug & (m)) { \ printf("%s:%s: " fmt, \ (sc) ? (const char *)(sc)->sc_name : \ (const char *)"umassX", \ __FUNCTION__ ,## __VA_ARGS__); \ } \ } while(0) #define UDMASS_GEN 0x00010000 /* general */ #define UDMASS_SCSI 0x00020000 /* scsi */ #define UDMASS_UFI 0x00040000 /* ufi command set */ #define UDMASS_ATAPI 0x00080000 /* 8070i command set */ #define UDMASS_CMD (UDMASS_SCSI|UDMASS_UFI|UDMASS_ATAPI) #define UDMASS_USB 0x00100000 /* USB general */ #define UDMASS_BBB 0x00200000 /* Bulk-Only transfers */ #define UDMASS_CBI 0x00400000 /* CBI transfers */ #define UDMASS_WIRE (UDMASS_BBB|UDMASS_CBI) #define UDMASS_ALL 0xffff0000 /* all of the above */ static int umass_debug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, umass, CTLFLAG_RW, 0, "USB umass"); SYSCTL_INT(_hw_usb_umass, OID_AUTO, debug, CTLFLAG_RW, &umass_debug, 0, "umass debug level"); #else #define DIF(...) /* nop */ #define DPRINTF(...) /* nop */ #endif #define UMASS_BULK_SIZE (1 << 17) /* bytes, must not be less than (1<<16) */ #define UMASS_CBI_DIAGNOSTIC_CMDLEN 12 /* bytes */ #define UMASS_MAX_CMDLEN MAX(12, CAM_MAX_CDBLEN) /* bytes */ /* USB transfer definitions */ #define UMASS_T_BBB_RESET1 0 /* Bulk-Only */ #define UMASS_T_BBB_RESET2 1 #define UMASS_T_BBB_RESET3 2 #define UMASS_T_BBB_COMMAND 3 #define UMASS_T_BBB_DATA_READ 4 #define UMASS_T_BBB_DATA_RD_CS 5 #define UMASS_T_BBB_DATA_WRITE 6 #define UMASS_T_BBB_DATA_WR_CS 7 #define UMASS_T_BBB_STATUS 8 #define UMASS_T_BBB_MAX 9 #define UMASS_T_CBI_RESET1 0 /* CBI */ #define UMASS_T_CBI_RESET2 1 #define UMASS_T_CBI_RESET3 2 #define UMASS_T_CBI_COMMAND 3 #define UMASS_T_CBI_DATA_READ 4 #define UMASS_T_CBI_DATA_RD_CS 5 #define UMASS_T_CBI_DATA_WRITE 6 #define UMASS_T_CBI_DATA_WR_CS 7 #define UMASS_T_CBI_STATUS 8 #define UMASS_T_CBI_RESET4 9 #define UMASS_T_CBI_MAX 10 #define UMASS_T_MAX MAX(UMASS_T_CBI_MAX, UMASS_T_BBB_MAX) /* Generic definitions */ /* Direction for transfer */ #define DIR_NONE 0 #define DIR_IN 1 #define DIR_OUT 2 /* device name */ #define DEVNAME "umass" #define DEVNAME_SIM "umass-sim" /* Approximate maximum transfer speeds (assumes 33% overhead). */ #define UMASS_FULL_TRANSFER_SPEED 1000 #define UMASS_HIGH_TRANSFER_SPEED 40000 #define UMASS_FLOPPY_TRANSFER_SPEED 20 #define UMASS_TIMEOUT 5000 /* ms */ /* CAM specific definitions */ #define UMASS_SCSIID_MAX 1 /* maximum number of drives expected */ #define UMASS_SCSIID_HOST UMASS_SCSIID_MAX /* Bulk-Only features */ #define UR_BBB_RESET 0xff /* Bulk-Only reset */ #define UR_BBB_GET_MAX_LUN 0xfe /* Get maximum lun */ /* Command Block Wrapper */ typedef struct { uDWord dCBWSignature; # define CBWSIGNATURE 0x43425355 uDWord dCBWTag; uDWord dCBWDataTransferLength; uByte bCBWFlags; # define CBWFLAGS_OUT 0x00 # define CBWFLAGS_IN 0x80 uByte bCBWLUN; uByte bCDBLength; # define CBWCDBLENGTH 16 uByte CBWCDB[CBWCDBLENGTH]; } UPACKED umass_bbb_cbw_t; #define UMASS_BBB_CBW_SIZE 31 /* Command Status Wrapper */ typedef struct { uDWord dCSWSignature; # define CSWSIGNATURE 0x53425355 # define CSWSIGNATURE_IMAGINATION_DBX1 0x43425355 # define CSWSIGNATURE_OLYMPUS_C1 0x55425355 uDWord dCSWTag; uDWord dCSWDataResidue; uByte bCSWStatus; # define CSWSTATUS_GOOD 0x0 # define CSWSTATUS_FAILED 0x1 # define CSWSTATUS_PHASE 0x2 } UPACKED umass_bbb_csw_t; #define UMASS_BBB_CSW_SIZE 13 /* CBI features */ #define UR_CBI_ADSC 0x00 typedef union { struct { u_int8_t type; # define IDB_TYPE_CCI 0x00 u_int8_t value; # define IDB_VALUE_PASS 0x00 # define IDB_VALUE_FAIL 0x01 # define IDB_VALUE_PHASE 0x02 # define IDB_VALUE_PERSISTENT 0x03 # define IDB_VALUE_STATUS_MASK 0x03 } UPACKED common; struct { u_int8_t asc; u_int8_t ascq; } UPACKED ufi; } UPACKED umass_cbi_sbl_t; struct umass_softc; /* see below */ typedef void (umass_callback_t) (struct umass_softc *sc, union ccb *ccb, u_int32_t residue, u_int8_t status); #define STATUS_CMD_OK 0 /* everything ok */ #define STATUS_CMD_UNKNOWN 1 /* will have to fetch sense */ #define STATUS_CMD_FAILED 2 /* transfer was ok, command failed */ #define STATUS_WIRE_FAILED 3 /* couldn't even get command across */ typedef u_int8_t (umass_transform_t) (struct umass_softc *sc, u_int8_t *cmd_ptr, u_int8_t cmd_len); struct umass_devdescr { u_int32_t vid; # define VID_WILDCARD 0xffffffff # define VID_EOT 0xfffffffe u_int32_t pid; # define PID_WILDCARD 0xffffffff # define PID_EOT 0xfffffffe u_int32_t rid; # define RID_WILDCARD 0xffffffff # define RID_EOT 0xfffffffe /* wire and command protocol */ u_int16_t proto; # define UMASS_PROTO_BBB 0x0001 /* USB wire protocol */ # define UMASS_PROTO_CBI 0x0002 # define UMASS_PROTO_CBI_I 0x0004 # define UMASS_PROTO_WIRE 0x00ff /* USB wire protocol mask */ # define UMASS_PROTO_SCSI 0x0100 /* command protocol */ # define UMASS_PROTO_ATAPI 0x0200 # define UMASS_PROTO_UFI 0x0400 # define UMASS_PROTO_RBC 0x0800 # define UMASS_PROTO_COMMAND 0xff00 /* command protocol mask */ /* Device specific quirks */ u_int16_t quirks; # define NO_QUIRKS 0x0000 /* The drive does not support Test Unit Ready. Convert to Start Unit */ # define NO_TEST_UNIT_READY 0x0001 /* The drive does not reset the Unit Attention state after REQUEST * SENSE has been sent. The INQUIRY command does not reset the UA * either, and so CAM runs in circles trying to retrieve the initial * INQUIRY data. */ # define RS_NO_CLEAR_UA 0x0002 /* The drive does not support START STOP. */ # define NO_START_STOP 0x0004 /* Don't ask for full inquiry data (255b). */ # define FORCE_SHORT_INQUIRY 0x0008 /* Needs to be initialised the Shuttle way */ # define SHUTTLE_INIT 0x0010 /* Drive needs to be switched to alternate iface 1 */ # define ALT_IFACE_1 0x0020 /* Drive does not do 1Mb/s, but just floppy speeds (20kb/s) */ # define FLOPPY_SPEED 0x0040 /* The device can't count and gets the residue of transfers wrong */ # define IGNORE_RESIDUE 0x0080 /* No GetMaxLun call */ # define NO_GETMAXLUN 0x0100 /* The device uses a weird CSWSIGNATURE. */ # define WRONG_CSWSIG 0x0200 /* Device cannot handle INQUIRY so fake a generic response */ # define NO_INQUIRY 0x0400 /* Device cannot handle INQUIRY EVPD, return CHECK CONDITION */ # define NO_INQUIRY_EVPD 0x0800 /* Pad all RBC requests to 12 bytes. */ # define RBC_PAD_TO_12 0x1000 }; static const struct umass_devdescr umass_devdescr[] = { { USB_VENDOR_ASAHIOPTICAL, PID_WILDCARD, RID_WILDCARD, UMASS_PROTO_ATAPI | UMASS_PROTO_CBI_I, RS_NO_CLEAR_UA }, { USB_VENDOR_ADDON, USB_PRODUCT_ADDON_ATTACHE, RID_WILDCARD, UMASS_PROTO_SCSI | UMASS_PROTO_BBB, IGNORE_RESIDUE }, { USB_VENDOR_FUJIPHOTO, USB_PRODUCT_FUJIPHOTO_MASS0100, RID_WILDCARD, UMASS_PROTO_ATAPI | UMASS_PROTO_CBI_I, RS_NO_CLEAR_UA }, { USB_VENDOR_ADDON, USB_PRODUCT_ADDON_A256MB, RID_WILDCARD, UMASS_PROTO_SCSI | UMASS_PROTO_BBB, IGNORE_RESIDUE }, { USB_VENDOR_ADDON, USB_PRODUCT_ADDON_DISKPRO512, RID_WILDCARD, UMASS_PROTO_SCSI | UMASS_PROTO_BBB, IGNORE_RESIDUE }, { USB_VENDOR_GENESYS, USB_PRODUCT_GENESYS_GL641USB2IDE, RID_WILDCARD, UMASS_PROTO_SCSI | UMASS_PROTO_BBB, FORCE_SHORT_INQUIRY | NO_START_STOP | IGNORE_RESIDUE }, { USB_VENDOR_GENESYS, USB_PRODUCT_GENESYS_GL641USB2IDE_2, RID_WILDCARD, UMASS_PROTO_ATAPI | UMASS_PROTO_BBB, FORCE_SHORT_INQUIRY | NO_START_STOP | IGNORE_RESIDUE }, { USB_VENDOR_GENESYS, USB_PRODUCT_GENESYS_GL641USB, RID_WILDCARD, UMASS_PROTO_SCSI | UMASS_PROTO_BBB, FORCE_SHORT_INQUIRY | NO_START_STOP | IGNORE_RESIDUE }, { USB_VENDOR_GENESYS, USB_PRODUCT_GENESYS_GL641USB_2, RID_WILDCARD, UMASS_PROTO_SCSI | UMASS_PROTO_BBB, WRONG_CSWSIG }, { USB_VENDOR_HITACHI, USB_PRODUCT_HITACHI_DVDCAM_USB, RID_WILDCARD, UMASS_PROTO_ATAPI | UMASS_PROTO_CBI_I, NO_INQUIRY }, { USB_VENDOR_HP, USB_PRODUCT_HP_CDW8200, RID_WILDCARD, UMASS_PROTO_ATAPI | UMASS_PROTO_CBI_I, NO_TEST_UNIT_READY | NO_START_STOP }, { USB_VENDOR_IMAGINATION, USB_PRODUCT_IMAGINATION_DBX1, RID_WILDCARD, UMASS_PROTO_SCSI | UMASS_PROTO_BBB, WRONG_CSWSIG }, { USB_VENDOR_INSYSTEM, USB_PRODUCT_INSYSTEM_USBCABLE, RID_WILDCARD, UMASS_PROTO_ATAPI | UMASS_PROTO_CBI, NO_TEST_UNIT_READY | NO_START_STOP | ALT_IFACE_1 }, { USB_VENDOR_IODATA, USB_PRODUCT_IODATA_IU_CD2, RID_WILDCARD, UMASS_PROTO_SCSI | UMASS_PROTO_BBB, NO_QUIRKS }, { USB_VENDOR_IODATA, USB_PRODUCT_IODATA_DVR_UEH8, RID_WILDCARD, UMASS_PROTO_SCSI | UMASS_PROTO_BBB, NO_QUIRKS }, { USB_VENDOR_IOMEGA, USB_PRODUCT_IOMEGA_ZIP100, RID_WILDCARD, /* XXX This is not correct as there are Zip drives that use ATAPI. */ UMASS_PROTO_SCSI | UMASS_PROTO_BBB, NO_TEST_UNIT_READY }, { USB_VENDOR_LOGITEC, USB_PRODUCT_LOGITEC_LDR_H443SU2, RID_WILDCARD, UMASS_PROTO_SCSI, NO_QUIRKS }, { USB_VENDOR_LOGITEC, USB_PRODUCT_LOGITEC_LDR_H443U2, RID_WILDCARD, UMASS_PROTO_SCSI | UMASS_PROTO_BBB, NO_QUIRKS }, { USB_VENDOR_MELCO, USB_PRODUCT_MELCO_DUBPXXG, RID_WILDCARD, UMASS_PROTO_SCSI | UMASS_PROTO_BBB, FORCE_SHORT_INQUIRY | NO_START_STOP | IGNORE_RESIDUE }, { USB_VENDOR_MICROTECH, USB_PRODUCT_MICROTECH_DPCM, RID_WILDCARD, UMASS_PROTO_SCSI | UMASS_PROTO_CBI, NO_TEST_UNIT_READY | NO_START_STOP }, { USB_VENDOR_MOTOROLA2, USB_PRODUCT_MOTOROLA2_E398, RID_WILDCARD, UMASS_PROTO_SCSI | UMASS_PROTO_BBB, FORCE_SHORT_INQUIRY | NO_INQUIRY_EVPD | NO_GETMAXLUN }, { USB_VENDOR_MSYSTEMS, USB_PRODUCT_MSYSTEMS_DISKONKEY, RID_WILDCARD, UMASS_PROTO_SCSI | UMASS_PROTO_BBB, IGNORE_RESIDUE | NO_GETMAXLUN | RS_NO_CLEAR_UA }, { USB_VENDOR_MSYSTEMS, USB_PRODUCT_MSYSTEMS_DISKONKEY2, RID_WILDCARD, UMASS_PROTO_ATAPI | UMASS_PROTO_BBB, NO_QUIRKS }, { USB_VENDOR_NEODIO, USB_PRODUCT_NEODIO_ND3260, RID_WILDCARD, UMASS_PROTO_SCSI | UMASS_PROTO_BBB, FORCE_SHORT_INQUIRY }, { USB_VENDOR_OLYMPUS, USB_PRODUCT_OLYMPUS_C1, RID_WILDCARD, UMASS_PROTO_SCSI | UMASS_PROTO_BBB, WRONG_CSWSIG }, { USB_VENDOR_ONSPEC, USB_PRODUCT_ONSPEC_UCF100, RID_WILDCARD, UMASS_PROTO_ATAPI | UMASS_PROTO_BBB, NO_INQUIRY | NO_GETMAXLUN }, { USB_VENDOR_PANASONIC, USB_PRODUCT_PANASONIC_KXLCB20AN, RID_WILDCARD, UMASS_PROTO_SCSI | UMASS_PROTO_BBB, NO_QUIRKS }, { USB_VENDOR_PANASONIC, USB_PRODUCT_PANASONIC_KXLCB35AN, RID_WILDCARD, UMASS_PROTO_SCSI | UMASS_PROTO_BBB, NO_QUIRKS }, { USB_VENDOR_PLEXTOR, USB_PRODUCT_PLEXTOR_40_12_40U, RID_WILDCARD, UMASS_PROTO_SCSI | UMASS_PROTO_BBB, NO_TEST_UNIT_READY }, { USB_VENDOR_PNY, USB_PRODUCT_PNY_ATTACHE2, RID_WILDCARD, UMASS_PROTO_SCSI | UMASS_PROTO_BBB, IGNORE_RESIDUE | NO_START_STOP }, { USB_VENDOR_SANDISK, USB_PRODUCT_SANDISK_SDCZ2_256, RID_WILDCARD, UMASS_PROTO_SCSI | UMASS_PROTO_BBB, IGNORE_RESIDUE }, { USB_VENDOR_SANDISK, USB_PRODUCT_SANDISK_SDCZ4_128, RID_WILDCARD, UMASS_PROTO_SCSI | UMASS_PROTO_BBB, IGNORE_RESIDUE }, { USB_VENDOR_SANDISK, USB_PRODUCT_SANDISK_SDCZ4_256, RID_WILDCARD, UMASS_PROTO_SCSI | UMASS_PROTO_BBB, IGNORE_RESIDUE }, { USB_VENDOR_SCANLOGIC, USB_PRODUCT_SCANLOGIC_SL11R, RID_WILDCARD, UMASS_PROTO_ATAPI | UMASS_PROTO_BBB, NO_INQUIRY }, { USB_VENDOR_SHUTTLE, USB_PRODUCT_SHUTTLE_EUSB, RID_WILDCARD, UMASS_PROTO_ATAPI | UMASS_PROTO_CBI_I, NO_TEST_UNIT_READY | NO_START_STOP | SHUTTLE_INIT }, { USB_VENDOR_SIGMATEL, USB_PRODUCT_SIGMATEL_I_BEAD100, RID_WILDCARD, UMASS_PROTO_SCSI | UMASS_PROTO_BBB, SHUTTLE_INIT }, { USB_VENDOR_SIIG, USB_PRODUCT_SIIG_WINTERREADER, RID_WILDCARD, UMASS_PROTO_SCSI | UMASS_PROTO_BBB, IGNORE_RESIDUE }, { USB_VENDOR_SONY, USB_PRODUCT_SONY_DSC, 0x0500, UMASS_PROTO_RBC | UMASS_PROTO_CBI, RBC_PAD_TO_12 }, { USB_VENDOR_SONY, USB_PRODUCT_SONY_DSC, 0x0600, UMASS_PROTO_RBC | UMASS_PROTO_CBI, RBC_PAD_TO_12 }, { USB_VENDOR_SONY, USB_PRODUCT_SONY_DSC, RID_WILDCARD, UMASS_PROTO_RBC | UMASS_PROTO_CBI, NO_QUIRKS }, { USB_VENDOR_SONY, USB_PRODUCT_SONY_HANDYCAM, RID_WILDCARD, UMASS_PROTO_RBC | UMASS_PROTO_CBI, NO_QUIRKS }, { USB_VENDOR_SONY, USB_PRODUCT_SONY_MSC, RID_WILDCARD, UMASS_PROTO_RBC | UMASS_PROTO_CBI, NO_QUIRKS }, { USB_VENDOR_TREK, USB_PRODUCT_TREK_THUMBDRIVE_8MB, RID_WILDCARD, UMASS_PROTO_ATAPI | UMASS_PROTO_BBB, IGNORE_RESIDUE }, { USB_VENDOR_TRUMPION, USB_PRODUCT_TRUMPION_C3310, RID_WILDCARD, UMASS_PROTO_UFI | UMASS_PROTO_CBI, NO_QUIRKS }, { USB_VENDOR_TWINMOS, USB_PRODUCT_TWINMOS_MDIV, RID_WILDCARD, UMASS_PROTO_SCSI | UMASS_PROTO_BBB, NO_QUIRKS }, { USB_VENDOR_WESTERN, USB_PRODUCT_WESTERN_EXTHDD, RID_WILDCARD, UMASS_PROTO_SCSI | UMASS_PROTO_BBB, FORCE_SHORT_INQUIRY | NO_START_STOP | IGNORE_RESIDUE }, { USB_VENDOR_YANO, USB_PRODUCT_YANO_U640MO, RID_WILDCARD, UMASS_PROTO_ATAPI | UMASS_PROTO_CBI_I, FORCE_SHORT_INQUIRY }, { VID_EOT, PID_EOT, RID_EOT, 0, 0 } }; struct umass_softc { struct scsi_sense cam_scsi_sense; struct scsi_test_unit_ready cam_scsi_test_unit_ready; struct mtx sc_mtx; struct { u_int8_t * data_ptr; union ccb * ccb; umass_callback_t * callback; u_int32_t data_len; /* bytes */ u_int32_t data_rem; /* bytes */ u_int32_t data_timeout; /* ms */ u_int32_t actlen; /* bytes */ u_int8_t cmd_data[UMASS_MAX_CMDLEN]; u_int8_t cmd_len; /* bytes */ u_int8_t dir; u_int8_t lun; } sc_transfer; /* Bulk specific variables for transfers in progress */ umass_bbb_cbw_t cbw; /* command block wrapper */ umass_bbb_csw_t csw; /* command status wrapper*/ /* CBI specific variables for transfers in progress */ umass_cbi_sbl_t sbl; /* status block */ device_t sc_dev; struct usbd_device * sc_udev; struct cam_sim * sc_sim; /* SCSI Interface Module */ struct usbd_xfer * sc_xfer[UMASS_T_MAX]; /* The command transform function is used to convert the SCSI commands * into their derivatives, like UFI, ATAPI, and friends. */ umass_transform_t * sc_transform; u_int32_t sc_unit; u_int16_t sc_proto; /* wire and cmd protocol */ u_int16_t sc_quirks; /* they got it almost right */ u_int8_t sc_name[16]; u_int8_t sc_iface_no; /* interface number */ u_int8_t sc_maxlun; /* maximum LUN number, inclusive */ u_int8_t sc_last_xfer_index; u_int8_t sc_reset_count; u_int8_t sc_status_try; }; struct umass_probe_proto { u_int16_t quirks; u_int16_t proto; int32_t error; }; /* prototypes */ static device_probe_t umass_probe; static device_attach_t umass_attach; static device_detach_t umass_detach; static void umass_init_shuttle(struct umass_softc *sc); static void umass_reset(struct umass_softc *sc); static void umass_tr_error(struct usbd_xfer *xfer); static void umass_t_bbb_reset1_callback(struct usbd_xfer *xfer); static void umass_t_bbb_reset2_callback(struct usbd_xfer *xfer); static void umass_t_bbb_reset3_callback(struct usbd_xfer *xfer); static void umass_t_bbb_data_clear_stall_callback(struct usbd_xfer *xfer, u_int8_t next_xfer, u_int8_t stall_xfer); static void umass_t_bbb_command_callback(struct usbd_xfer *xfer); static void umass_t_bbb_data_read_callback(struct usbd_xfer *xfer); static void umass_t_bbb_data_rd_cs_callback(struct usbd_xfer *xfer); static void umass_t_bbb_data_write_callback(struct usbd_xfer *xfer); static void umass_t_bbb_data_wr_cs_callback(struct usbd_xfer *xfer); static void umass_t_bbb_status_callback(struct usbd_xfer *xfer); static void umass_command_start(struct umass_softc *sc, u_int8_t dir, void *data_ptr, u_int32_t data_len, u_int32_t data_timeout, umass_callback_t *callback, union ccb *ccb); static u_int8_t umass_bbb_get_max_lun(struct umass_softc *sc); static void umass_cbi_start_status(struct umass_softc *sc); static void umass_t_cbi_reset1_callback(struct usbd_xfer *xfer); static void umass_t_cbi_reset2_callback(struct usbd_xfer *xfer); static void umass_t_cbi_reset3_callback(struct usbd_xfer *xfer); static void umass_t_cbi_reset4_callback(struct usbd_xfer *xfer); static void umass_t_cbi_data_clear_stall_callback(struct usbd_xfer *xfer, u_int8_t next_xfer, u_int8_t stall_xfer); static void umass_t_cbi_command_callback(struct usbd_xfer *xfer); static void umass_t_cbi_data_read_callback(struct usbd_xfer *xfer); static void umass_t_cbi_data_rd_cs_callback(struct usbd_xfer *xfer); static void umass_t_cbi_data_write_callback(struct usbd_xfer *xfer); static void umass_t_cbi_data_wr_cs_callback(struct usbd_xfer *xfer); static void umass_t_cbi_status_callback(struct usbd_xfer *xfer); static int umass_cam_attach_sim(struct umass_softc *sc); static void umass_cam_rescan_callback(struct cam_periph *periph, union ccb *ccb); static void umass_cam_rescan(struct umass_softc *sc); static void umass_cam_attach(struct umass_softc *sc); static void umass_cam_detach_sim(struct umass_softc *sc); static void umass_cam_action(struct cam_sim *sim, union ccb *ccb); static void umass_cam_poll(struct cam_sim *sim); static void umass_cam_cb(struct umass_softc *sc, union ccb *ccb, u_int32_t residue, u_int8_t status); static void umass_cam_sense_cb(struct umass_softc *sc, union ccb *ccb, u_int32_t residue, u_int8_t status); static void umass_cam_quirk_cb(struct umass_softc *sc, union ccb *ccb, u_int32_t residue, u_int8_t status); static u_int8_t umass_scsi_transform(struct umass_softc *sc, u_int8_t *cmd_ptr, u_int8_t cmd_len); static u_int8_t umass_rbc_transform(struct umass_softc *sc, u_int8_t *cmd_ptr, u_int8_t cmd_len); static u_int8_t umass_ufi_transform(struct umass_softc *sc, u_int8_t *cmd_ptr, u_int8_t cmd_len); static u_int8_t umass_atapi_transform(struct umass_softc *sc, u_int8_t *cmd_ptr, u_int8_t cmd_len); static u_int8_t umass_no_transform(struct umass_softc *sc, u_int8_t *cmd, u_int8_t cmdlen); #ifdef USB_DEBUG static void umass_bbb_dump_cbw(struct umass_softc *sc, umass_bbb_cbw_t *cbw); static void umass_bbb_dump_csw(struct umass_softc *sc, umass_bbb_csw_t *csw); static void umass_cbi_dump_cmd(struct umass_softc *sc, void *cmd, u_int8_t cmdlen); static void umass_dump_buffer(struct umass_softc *sc, u_int8_t *buffer, u_int32_t buflen, u_int32_t printlen); #endif struct usbd_config umass_bbb_config[UMASS_T_BBB_MAX] = { [UMASS_T_BBB_RESET1] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .flags = USBD_USE_DMA, .callback = &umass_t_bbb_reset1_callback, .timeout = 5000, /* 5 seconds */ }, [UMASS_T_BBB_RESET2] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .flags = USBD_USE_DMA, .callback = &umass_t_bbb_reset2_callback, .timeout = 5000, /* 5 seconds */ }, [UMASS_T_BBB_RESET3] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .flags = USBD_USE_DMA, .callback = &umass_t_bbb_reset3_callback, .timeout = 5000, /* 5 seconds */ }, [UMASS_T_BBB_COMMAND] = { .type = UE_BULK, .endpoint = -1, /* any */ .direction = UE_DIR_OUT, .bufsize = sizeof(umass_bbb_cbw_t), .flags = USBD_USE_DMA, .callback = &umass_t_bbb_command_callback, .timeout = 5000, /* 5 seconds */ }, [UMASS_T_BBB_DATA_READ] = { .type = UE_BULK, .endpoint = -1, /* any */ .direction = UE_DIR_IN, .bufsize = UMASS_BULK_SIZE, .flags = (USBD_USE_DMA|USBD_SHORT_XFER_OK), .callback = &umass_t_bbb_data_read_callback, .timeout = 0, /* overwritten later */ }, [UMASS_T_BBB_DATA_RD_CS] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .flags = USBD_USE_DMA, .callback = &umass_t_bbb_data_rd_cs_callback, .timeout = 5000, /* 5 seconds */ }, [UMASS_T_BBB_DATA_WRITE] = { .type = UE_BULK, .endpoint = -1, /* any */ .direction = UE_DIR_OUT, .bufsize = UMASS_BULK_SIZE, .flags = USBD_USE_DMA, .callback = &umass_t_bbb_data_write_callback, .timeout = 0, /* overwritten later */ }, [UMASS_T_BBB_DATA_WR_CS] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .flags = USBD_USE_DMA, .callback = &umass_t_bbb_data_wr_cs_callback, .timeout = 5000, /* 5 seconds */ }, [UMASS_T_BBB_STATUS] = { .type = UE_BULK, .endpoint = -1, /* any */ .direction = UE_DIR_IN, .bufsize = sizeof(umass_bbb_csw_t), .flags = (USBD_USE_DMA|USBD_SHORT_XFER_OK), .callback = &umass_t_bbb_status_callback, .timeout = 5000, /* ms */ }, }; struct usbd_config umass_cbi_config[UMASS_T_CBI_MAX] = { [UMASS_T_CBI_RESET1] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = (sizeof(usb_device_request_t) + UMASS_CBI_DIAGNOSTIC_CMDLEN), .flags = USBD_USE_DMA, .callback = &umass_t_cbi_reset1_callback, .timeout = 5000, /* 5 seconds */ }, [UMASS_T_CBI_RESET2] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .flags = USBD_USE_DMA, .callback = &umass_t_cbi_reset2_callback, .timeout = 5000, /* 5 seconds */ }, [UMASS_T_CBI_RESET3] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .flags = USBD_USE_DMA, .callback = &umass_t_cbi_reset3_callback, .timeout = 5000, /* 5 seconds */ }, [UMASS_T_CBI_COMMAND] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = (sizeof(usb_device_request_t) + UMASS_MAX_CMDLEN), .flags = USBD_USE_DMA, .callback = &umass_t_cbi_command_callback, .timeout = 5000, /* 5 seconds */ }, [UMASS_T_CBI_DATA_READ] = { .type = UE_BULK, .endpoint = -1, /* any */ .direction = UE_DIR_IN, .bufsize = UMASS_BULK_SIZE, .flags = (USBD_USE_DMA|USBD_SHORT_XFER_OK), .callback = &umass_t_cbi_data_read_callback, .timeout = 0, /* overwritten later */ }, [UMASS_T_CBI_DATA_RD_CS] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .flags = USBD_USE_DMA, .callback = &umass_t_cbi_data_rd_cs_callback, .timeout = 5000, /* 5 seconds */ }, [UMASS_T_CBI_DATA_WRITE] = { .type = UE_BULK, .endpoint = -1, /* any */ .direction = UE_DIR_OUT, .bufsize = UMASS_BULK_SIZE, .flags = USBD_USE_DMA, .callback = &umass_t_cbi_data_write_callback, .timeout = 0, /* overwritten later */ }, [UMASS_T_CBI_DATA_WR_CS] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .flags = USBD_USE_DMA, .callback = &umass_t_cbi_data_wr_cs_callback, .timeout = 5000, /* 5 seconds */ }, [UMASS_T_CBI_STATUS] = { .type = UE_INTERRUPT, .endpoint = -1, /* any */ .direction = UE_DIR_IN, .flags = (USBD_USE_DMA|USBD_SHORT_XFER_OK), .bufsize = sizeof(umass_cbi_sbl_t), .callback = &umass_t_cbi_status_callback, .timeout = 5000, /* ms */ }, [UMASS_T_CBI_RESET4] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .flags = USBD_USE_DMA, .callback = &umass_t_cbi_reset4_callback, .timeout = 5000, /* ms */ }, }; /* If device cannot return valid inquiry data, fake it */ static const u_int8_t fake_inq_data[SHORT_INQUIRY_LENGTH] = { 0, /*removable*/ 0x80, SCSI_REV_2, SCSI_REV_2, /*additional_length*/ 31, 0, 0, 0 }; #define UFI_COMMAND_LENGTH 12 /* UFI commands are always 12 bytes */ #define ATAPI_COMMAND_LENGTH 12 /* ATAPI commands are always 12 bytes */ static devclass_t umass_devclass; static device_method_t umass_methods[] = { /* Device interface */ DEVMETHOD(device_probe, umass_probe), DEVMETHOD(device_attach, umass_attach), DEVMETHOD(device_detach, umass_detach), { 0, 0 } }; static driver_t umass_driver = { .name = "umass", .methods = umass_methods, .size = sizeof(struct umass_softc), }; DRIVER_MODULE(umass, uhub, umass_driver, umass_devclass, usbd_driver_load, 0); MODULE_DEPEND(umass, usb, 1,1,1); MODULE_DEPEND(umass, cam, 1,1,1); /* * USB device probe/attach/detach */ /* * Match the device we are seeing with the * devices supported. */ static struct umass_probe_proto umass_probe_proto(device_t dev, struct usb_attach_arg *uaa) { const struct umass_devdescr *udd = umass_devdescr; usb_interface_descriptor_t *id; struct umass_probe_proto ret; bzero(&ret, sizeof(ret)); /* An entry specifically for Y-E Data devices as they don't fit in the * device description table. */ if ((uaa->vendor == USB_VENDOR_YEDATA) && (uaa->product == USB_PRODUCT_YEDATA_FLASHBUSTERU)) { /* Revisions < 1.28 do not handle the interrupt endpoint * very well. */ if (uaa->release < 0x128) { ret.proto = UMASS_PROTO_UFI | UMASS_PROTO_CBI; } else { ret.proto = UMASS_PROTO_UFI | UMASS_PROTO_CBI_I; } /* * Revisions < 1.28 do not have the TEST UNIT READY command * Revisions == 1.28 have a broken TEST UNIT READY */ if (uaa->release <= 0x128) { ret.quirks |= NO_TEST_UNIT_READY; } ret.quirks |= RS_NO_CLEAR_UA | FLOPPY_SPEED; ret.error = UMATCH_VENDOR_PRODUCT; goto done; } /* Check the list of supported devices for a match. While looking, * check for wildcarded and fully matched. First match wins. */ for ( ; udd->vid != VID_EOT; udd++) { if ((udd->vid == VID_WILDCARD) && (udd->pid == PID_WILDCARD) && (udd->rid == RID_WILDCARD)) { device_printf(dev, "ignoring invalid " "wildcard quirk\n"); continue; } if (((udd->vid == uaa->vendor) || (udd->vid == VID_WILDCARD)) && ((udd->pid == uaa->product) || (udd->pid == PID_WILDCARD))) { if (udd->rid == RID_WILDCARD) { ret.proto = udd->proto; ret.quirks = udd->quirks; ret.error = UMATCH_VENDOR_PRODUCT; goto done; } else if (udd->rid == uaa->release) { ret.proto = udd->proto; ret.quirks = udd->quirks; ret.error = UMATCH_VENDOR_PRODUCT_REV; goto done; } /* else RID does not match */ } } /* Check for a standards compliant device */ id = usbd_get_interface_descriptor(uaa->iface); if ((id == NULL) || (id->bInterfaceClass != UICLASS_MASS)) { ret.error = UMATCH_NONE; goto done; } switch (id->bInterfaceSubClass) { case UISUBCLASS_SCSI: ret.proto |= UMASS_PROTO_SCSI; break; case UISUBCLASS_UFI: ret.proto |= UMASS_PROTO_UFI; break; case UISUBCLASS_RBC: ret.proto |= UMASS_PROTO_RBC; break; case UISUBCLASS_SFF8020I: case UISUBCLASS_SFF8070I: ret.proto |= UMASS_PROTO_ATAPI; break; default: device_printf(dev, "unsupported command " "protocol %d\n", id->bInterfaceSubClass); ret.error = UMATCH_NONE; goto done; } switch (id->bInterfaceProtocol) { case UIPROTO_MASS_CBI: ret.proto |= UMASS_PROTO_CBI; break; case UIPROTO_MASS_CBI_I: ret.proto |= UMASS_PROTO_CBI_I; break; case UIPROTO_MASS_BBB_OLD: case UIPROTO_MASS_BBB: ret.proto |= UMASS_PROTO_BBB; break; default: device_printf(dev, "unsupported wire " "protocol %d\n", id->bInterfaceProtocol); ret.error = UMATCH_NONE; goto done; } ret.error = UMATCH_DEVCLASS_DEVSUBCLASS_DEVPROTO; done: return ret; } static int umass_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct umass_probe_proto temp; if (uaa->iface == NULL) { return UMATCH_NONE; } temp = umass_probe_proto(dev, uaa); return (temp.error); } static int umass_attach(device_t dev) { struct umass_softc *sc = device_get_softc(dev); struct usb_attach_arg *uaa = device_get_ivars(dev); struct umass_probe_proto temp = umass_probe_proto(dev, uaa); usb_interface_descriptor_t *id; int32_t err; if (sc == NULL) { return ENOMEM; } /* * NOTE: the softc struct is bzero-ed in device_set_driver. * We can safely call umass_detach without specifically * initializing the struct. */ sc->sc_dev = dev; sc->sc_udev = uaa->device; sc->sc_proto = temp.proto; sc->sc_quirks = temp.quirks; sc->sc_unit = device_get_unit(dev); snprintf(sc->sc_name, sizeof(sc->sc_name), "%s", device_get_nameunit(dev)); usbd_set_desc(dev, uaa->device); mtx_init(&(sc->sc_mtx), "UMASS lock", NULL, (MTX_DEF|MTX_RECURSE)); /* get interface index */ id = usbd_get_interface_descriptor(uaa->iface); if (id == NULL) { device_printf(dev, "failed to get " "interface number\n"); goto detach; } sc->sc_iface_no = id->bInterfaceNumber; #ifdef USB_DEBUG device_printf(dev, " "); switch (sc->sc_proto & UMASS_PROTO_COMMAND) { case UMASS_PROTO_SCSI: printf("SCSI"); break; case UMASS_PROTO_ATAPI: printf("8070i (ATAPI)"); break; case UMASS_PROTO_UFI: printf("UFI"); break; case UMASS_PROTO_RBC: printf("RBC"); break; default: printf("(unknown 0x%02x)", sc->sc_proto & UMASS_PROTO_COMMAND); break; } printf(" over "); switch (sc->sc_proto & UMASS_PROTO_WIRE) { case UMASS_PROTO_BBB: printf("Bulk-Only"); break; case UMASS_PROTO_CBI: /* uses Comand/Bulk pipes */ printf("CBI"); break; case UMASS_PROTO_CBI_I: /* uses Comand/Bulk/Interrupt pipes */ printf("CBI with CCI"); break; default: printf("(unknown 0x%02x)", sc->sc_proto & UMASS_PROTO_WIRE); } printf("; quirks = 0x%04x\n", sc->sc_quirks); #endif if (sc->sc_quirks & ALT_IFACE_1) { err = usbreq_set_interface(uaa->device, uaa->iface_index, 1); if (err) { DPRINTF(sc, UDMASS_USB, "could not switch to " "Alt Interface 1\n"); goto detach; } } /* allocate all required USB transfers */ if (sc->sc_proto & UMASS_PROTO_BBB) { err = usbd_transfer_setup (uaa->device, uaa->iface_index, sc->sc_xfer, umass_bbb_config, UMASS_T_BBB_MAX, sc, &(sc->sc_mtx)); /* skip reset first time */ sc->sc_last_xfer_index = UMASS_T_BBB_COMMAND; } else if (sc->sc_proto & (UMASS_PROTO_CBI|UMASS_PROTO_CBI_I)) { err = usbd_transfer_setup (uaa->device, uaa->iface_index, sc->sc_xfer, umass_bbb_config, (sc->sc_proto & UMASS_PROTO_CBI_I) ? UMASS_T_CBI_MAX : (UMASS_T_CBI_MAX-2), sc, &(sc->sc_mtx)); /* skip reset first time */ sc->sc_last_xfer_index = UMASS_T_CBI_COMMAND; } else { err = USBD_INVAL; } if (err) { device_printf(dev, "could not setup required " "transfers, %s\n", usbd_errstr(err)); goto detach; } sc->sc_transform = (sc->sc_proto & UMASS_PROTO_SCSI) ? &umass_scsi_transform : (sc->sc_proto & UMASS_PROTO_UFI) ? &umass_ufi_transform : (sc->sc_proto & UMASS_PROTO_ATAPI) ? &umass_atapi_transform : (sc->sc_proto & UMASS_PROTO_RBC) ? &umass_rbc_transform : &umass_no_transform; /* from here onwards the device can be used. */ if (sc->sc_quirks & SHUTTLE_INIT) { umass_init_shuttle(sc); } /* get the maximum LUN supported by the device */ if (((sc->sc_proto & UMASS_PROTO_WIRE) == UMASS_PROTO_BBB) && !(sc->sc_quirks & NO_GETMAXLUN)) sc->sc_maxlun = umass_bbb_get_max_lun(sc); else sc->sc_maxlun = 0; /* Prepare the SCSI command block */ sc->cam_scsi_sense.opcode = REQUEST_SENSE; sc->cam_scsi_test_unit_ready.opcode = TEST_UNIT_READY; /* some devices need a delay after that the * configuration value is set to * function properly: */ usbd_delay_ms(uaa->device, 1000); /* register the SIM */ err = umass_cam_attach_sim(sc); if (err) { goto detach; } /* scan the SIM */ umass_cam_attach(sc); DPRINTF(sc, UDMASS_GEN, "Attach finished\n"); return 0; /* success */ detach: umass_detach(dev); return ENXIO; /* failure */ } static int umass_detach(device_t dev) { struct umass_softc *sc = device_get_softc(dev); u_int8_t i; DPRINTF(sc, UDMASS_USB, "\n"); mtx_lock(&(sc->sc_mtx)); /* signal that the device is going away */ sc->sc_last_xfer_index = UMASS_T_MAX; for (i = 0; i < UMASS_T_MAX; i++) { if (sc->sc_xfer[i]) { usbd_transfer_stop(sc->sc_xfer[i]); } } mtx_unlock(&(sc->sc_mtx)); umass_cam_detach_sim(sc); usbd_transfer_unsetup(sc->sc_xfer, UMASS_T_MAX); mtx_destroy(&(sc->sc_mtx)); return 0; /* success */ } static void umass_init_shuttle(struct umass_softc *sc) { usb_device_request_t req; u_int8_t status[2] = { 0, 0 }; /* The Linux driver does this, but no one can tell us what the * command does. */ req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = 1; /* XXX unknown command */ USETW(req.wValue, 0); req.wIndex[0] = sc->sc_iface_no; req.wIndex[1] = 0; USETW(req.wLength, sizeof(status)); (void) usbd_do_request(sc->sc_udev, &req, &status); DPRINTF(sc, UDMASS_GEN, "Shuttle init returned 0x%02x%02x\n", status[0], status[1]); return; } /* * Generic functions to handle transfers */ static void umass_transfer_start(struct umass_softc *sc, u_int8_t xfer_index) { DPRINTF(sc, UDMASS_GEN, "transfer index = " "%d\n", xfer_index); sc->sc_last_xfer_index = xfer_index; usbd_transfer_start(sc->sc_xfer[xfer_index]); return; } static void umass_reset(struct umass_softc *sc) { DPRINTF(sc, UDMASS_GEN, "resetting device\n"); /* stop the last transfer, * if not already stopped: */ if (sc->sc_last_xfer_index < UMASS_T_MAX) { usbd_transfer_stop(sc->sc_xfer[sc->sc_last_xfer_index]); sc->sc_reset_count = 0; umass_transfer_start(sc, 0); } return; } static void umass_tr_error(struct usbd_xfer *xfer) { struct umass_softc *sc = xfer->priv_sc; union ccb *ccb = sc->sc_transfer.ccb; if (xfer->error != USBD_CANCELLED) { DPRINTF(sc, UDMASS_GEN, "transfer error, %s -> " "reset\n", usbd_errstr(xfer->error)); if (sc->sc_reset_count < 16) { /* start reset before any callback */ umass_transfer_start(sc, 0); } else { /* suspend reset until next command */ sc->sc_last_xfer_index = 0; sc->sc_reset_count = 0; printf("%s: timeout: giving up " "reset!\n", sc->sc_name); } } if (ccb) { sc->sc_transfer.ccb = NULL; (sc->sc_transfer.callback) (sc, ccb, (sc->sc_transfer.data_len - sc->sc_transfer.actlen), STATUS_WIRE_FAILED); } return; } /* * BBB protocol specific functions */ static void umass_t_bbb_reset1_callback(struct usbd_xfer *xfer) { struct umass_softc *sc = xfer->priv_sc; usb_device_request_t req; USBD_CHECK_STATUS(xfer); tr_error: umass_tr_error(xfer); return; tr_transferred: umass_transfer_start(sc, UMASS_T_BBB_RESET2); return; tr_setup: /* * Reset recovery (5.3.4 in Universal Serial Bus Mass Storage Class) * * For Reset Recovery the host shall issue in the following order: * a) a Bulk-Only Mass Storage Reset * b) a Clear Feature HALT to the Bulk-In endpoint * c) a Clear Feature HALT to the Bulk-Out endpoint * * This is done in 3 steps, using 3 transfers: * UMASS_T_BBB_RESET1 * UMASS_T_BBB_RESET2 * UMASS_T_BBB_RESET3 */ DPRINTF(sc, UDMASS_BBB, "BBB reset!\n"); sc->sc_reset_count ++; req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UR_BBB_RESET; /* bulk only reset */ USETW(req.wValue, 0); req.wIndex[0] = sc->sc_iface_no; req.wIndex[1] = 0; USETW(req.wLength, 0); usbd_copy_in(&(xfer->buf_data), 0, &req, sizeof(req)); usbd_start_hardware(xfer); return; } static void umass_t_bbb_reset2_callback(struct usbd_xfer *xfer) { umass_t_bbb_data_clear_stall_callback(xfer, UMASS_T_BBB_RESET3, UMASS_T_BBB_DATA_READ); return; } static void umass_t_bbb_reset3_callback(struct usbd_xfer *xfer) { umass_t_bbb_data_clear_stall_callback(xfer, UMASS_T_BBB_COMMAND, UMASS_T_BBB_DATA_READ); return; } static void umass_t_bbb_data_clear_stall_callback(struct usbd_xfer *xfer, u_int8_t next_xfer, u_int8_t stall_xfer) { struct umass_softc *sc = xfer->priv_sc; USBD_CHECK_STATUS(xfer); tr_error: umass_tr_error(xfer); return; tr_transferred: umass_transfer_start(sc, next_xfer); return; tr_setup: usbd_clear_stall_tr_setup(xfer, sc->sc_xfer[stall_xfer]); usbd_clear_stall_tr_transferred(xfer, sc->sc_xfer[stall_xfer]); return; } static void umass_t_bbb_command_callback(struct usbd_xfer *xfer) { struct umass_softc *sc = xfer->priv_sc; union ccb *ccb = sc->sc_transfer.ccb; u_int32_t tag; USBD_CHECK_STATUS(xfer); tr_error: umass_tr_error(xfer); return; tr_transferred: umass_transfer_start (sc, ((sc->sc_transfer.dir == DIR_IN) ? UMASS_T_BBB_DATA_READ : (sc->sc_transfer.dir == DIR_OUT) ? UMASS_T_BBB_DATA_WRITE : UMASS_T_BBB_STATUS)); return; tr_setup: sc->sc_reset_count = 0; sc->sc_status_try = 0; if (ccb) { /* * the initial value is not important, * as long as the values are unique: */ tag = UGETDW(sc->cbw.dCBWTag) + 1; USETDW(sc->cbw.dCBWSignature, CBWSIGNATURE); USETDW(sc->cbw.dCBWTag, tag); /* * dCBWDataTransferLength: * This field indicates the number of bytes of data that the host * intends to transfer on the IN or OUT Bulk endpoint(as indicated by * the Direction bit) during the execution of this command. If this * field is set to 0, the device will expect that no data will be * transferred IN or OUT during this command, regardless of the value * of the Direction bit defined in dCBWFlags. */ USETDW(sc->cbw.dCBWDataTransferLength, sc->sc_transfer.data_len); /* * dCBWFlags: * The bits of the Flags field are defined as follows: * Bits 0-6 reserved * Bit 7 Direction - this bit shall be ignored if the * dCBWDataTransferLength field is zero. * 0 = data Out from host to device * 1 = data In from device to host */ sc->cbw.bCBWFlags = ((sc->sc_transfer.dir == DIR_IN) ? CBWFLAGS_IN : CBWFLAGS_OUT); sc->cbw.bCBWLUN = sc->sc_transfer.lun; if (sc->sc_transfer.cmd_len > sizeof(sc->cbw.CBWCDB)) { sc->sc_transfer.cmd_len = sizeof(sc->cbw.CBWCDB); DPRINTF(sc, UDMASS_BBB, "Truncating long command!\n"); } sc->cbw.bCDBLength = sc->sc_transfer.cmd_len; bcopy(sc->sc_transfer.cmd_data, sc->cbw.CBWCDB, sc->sc_transfer.cmd_len); bzero(sc->sc_transfer.cmd_data + sc->sc_transfer.cmd_len, sizeof(sc->cbw.CBWCDB) - sc->sc_transfer.cmd_len); DIF(UDMASS_BBB, umass_bbb_dump_cbw(sc, &sc->cbw);); usbd_copy_in(&(xfer->buf_data), 0, &(sc->cbw), sizeof(sc->cbw)); usbd_start_hardware(xfer); } return; } static void umass_t_bbb_data_read_callback(struct usbd_xfer *xfer) { struct umass_softc *sc = xfer->priv_sc; u_int32_t max_bulk = (UMASS_BULK_SIZE - (UMASS_BULK_SIZE % xfer->max_packet_size)); USBD_CHECK_STATUS(xfer); tr_error: if (xfer->error == USBD_CANCELLED) { umass_tr_error(xfer); } else { umass_transfer_start(sc, UMASS_T_BBB_DATA_RD_CS); } return; tr_transferred: usbd_copy_out(&(xfer->buf_data), 0, sc->sc_transfer.data_ptr, xfer->actlen); sc->sc_transfer.data_rem -= xfer->actlen; sc->sc_transfer.data_ptr += xfer->actlen; sc->sc_transfer.actlen += xfer->actlen; if (xfer->actlen < xfer->length) { /* short transfer */ sc->sc_transfer.data_rem = 0; } tr_setup: DPRINTF(sc, UDMASS_BBB, "max_bulk=%d, data_rem=%d\n", max_bulk, sc->sc_transfer.data_rem); if (sc->sc_transfer.data_rem == 0) { umass_transfer_start(sc, UMASS_T_BBB_STATUS); return; } if (max_bulk > sc->sc_transfer.data_rem) { max_bulk = sc->sc_transfer.data_rem; } xfer->timeout = sc->sc_transfer.data_timeout; xfer->length = max_bulk; usbd_start_hardware(xfer); return; } static void umass_t_bbb_data_rd_cs_callback(struct usbd_xfer *xfer) { umass_t_bbb_data_clear_stall_callback(xfer, UMASS_T_BBB_STATUS, UMASS_T_BBB_DATA_READ); return; } static void umass_t_bbb_data_write_callback(struct usbd_xfer *xfer) { struct umass_softc *sc = xfer->priv_sc; u_int32_t max_bulk = (UMASS_BULK_SIZE - (UMASS_BULK_SIZE % xfer->max_packet_size)); USBD_CHECK_STATUS(xfer); tr_error: if (xfer->error == USBD_CANCELLED) { umass_tr_error(xfer); } else { umass_transfer_start(sc, UMASS_T_BBB_DATA_WR_CS); } return; tr_transferred: sc->sc_transfer.data_rem -= xfer->actlen; sc->sc_transfer.data_ptr += xfer->actlen; sc->sc_transfer.actlen += xfer->actlen; tr_setup: DPRINTF(sc, UDMASS_BBB, "max_bulk=%d, data_rem=%d\n", max_bulk, sc->sc_transfer.data_rem); if (sc->sc_transfer.data_rem == 0) { umass_transfer_start(sc, UMASS_T_BBB_STATUS); return; } if (max_bulk > sc->sc_transfer.data_rem) { max_bulk = sc->sc_transfer.data_rem; } xfer->timeout = sc->sc_transfer.data_timeout; xfer->length = max_bulk; usbd_copy_in(&(xfer->buf_data), 0, sc->sc_transfer.data_ptr, max_bulk); usbd_start_hardware(xfer); return; } static void umass_t_bbb_data_wr_cs_callback(struct usbd_xfer *xfer) { umass_t_bbb_data_clear_stall_callback(xfer, UMASS_T_BBB_STATUS, UMASS_T_BBB_DATA_WRITE); return; } static void umass_t_bbb_status_callback(struct usbd_xfer *xfer) { struct umass_softc *sc = xfer->priv_sc; union ccb *ccb = sc->sc_transfer.ccb; u_int32_t residue; USBD_CHECK_STATUS(xfer); tr_error: DPRINTF(sc, UDMASS_BBB, "Failed to read CSW: %s, try %d\n", usbd_errstr(xfer->error), sc->sc_status_try); if ((xfer->error == USBD_CANCELLED) || (sc->sc_status_try)) { umass_tr_error(xfer); } else { sc->sc_status_try = 1; umass_transfer_start(sc, UMASS_T_BBB_DATA_RD_CS); } return; tr_transferred: if (xfer->actlen < sizeof(sc->csw)) { bzero(&(sc->csw), sizeof(sc->csw)); } usbd_copy_out(&(xfer->buf_data), 0, &(sc->csw), xfer->actlen); DIF(UDMASS_BBB, umass_bbb_dump_csw(sc, &(sc->csw));); residue = UGETDW(sc->csw.dCSWDataResidue); if (!residue) { residue = (sc->sc_transfer.data_len - sc->sc_transfer.actlen); } if (residue > sc->sc_transfer.data_len) { DPRINTF(sc, UDMASS_BBB, "truncating residue from %d " "to %d bytes\n", residue, sc->sc_transfer.data_len); residue = sc->sc_transfer.data_len; } /* translate weird command-status signatures: */ if (sc->sc_quirks & WRONG_CSWSIG) { u_int32_t temp = UGETDW(sc->csw.dCSWSignature); if ((temp == CSWSIGNATURE_OLYMPUS_C1) || (temp == CSWSIGNATURE_IMAGINATION_DBX1)) { USETDW(sc->csw.dCSWSignature, CSWSIGNATURE); } } /* check CSW and handle eventual error */ if (UGETDW(sc->csw.dCSWSignature) != CSWSIGNATURE) { DPRINTF(sc, UDMASS_BBB, "bad CSW signature 0x%08x != 0x%08x\n", UGETDW(sc->csw.dCSWSignature), CSWSIGNATURE); /* Invalid CSW: Wrong signature or wrong tag might * indicate that we lost synchronization. * Reset the device. */ goto tr_error; } else if (UGETDW(sc->csw.dCSWTag) != UGETDW(sc->cbw.dCBWTag)) { DPRINTF(sc, UDMASS_BBB, "Invalid CSW: tag 0x%08x should be " "0x%08x\n", UGETDW(sc->csw.dCSWTag), UGETDW(sc->cbw.dCBWTag)); goto tr_error; } else if (sc->csw.bCSWStatus > CSWSTATUS_PHASE) { DPRINTF(sc, UDMASS_BBB, "Invalid CSW: status %d > %d\n", sc->csw.bCSWStatus, CSWSTATUS_PHASE); goto tr_error; } else if (sc->csw.bCSWStatus == CSWSTATUS_PHASE) { DPRINTF(sc, UDMASS_BBB, "Phase error, residue = " "%d\n", residue); goto tr_error; } else if (sc->sc_transfer.actlen > sc->sc_transfer.data_len) { DPRINTF(sc, UDMASS_BBB, "Buffer overrun %d > %d\n", sc->sc_transfer.actlen, sc->sc_transfer.data_len); goto tr_error; } else if (sc->csw.bCSWStatus == CSWSTATUS_FAILED) { DPRINTF(sc, UDMASS_BBB, "Command failed, residue = " "%d\n", residue); sc->sc_transfer.ccb = NULL; sc->sc_last_xfer_index = UMASS_T_BBB_COMMAND; (sc->sc_transfer.callback) (sc, ccb, residue, STATUS_CMD_FAILED); } else { sc->sc_transfer.ccb = NULL; sc->sc_last_xfer_index = UMASS_T_BBB_COMMAND; (sc->sc_transfer.callback) (sc, ccb, residue, STATUS_CMD_OK); } return; tr_setup: usbd_start_hardware(xfer); return; } static void umass_command_start(struct umass_softc *sc, u_int8_t dir, void *data_ptr, u_int32_t data_len, u_int32_t data_timeout, umass_callback_t *callback, union ccb *ccb) { sc->sc_transfer.lun = ccb->ccb_h.target_lun; /* * NOTE: assumes that "sc->sc_transfer.cmd_data" and * "sc->sc_transfer.cmd_len" has been properly * initialized. */ sc->sc_transfer.dir = data_len ? dir : DIR_NONE; sc->sc_transfer.data_ptr = data_ptr; sc->sc_transfer.data_len = data_len; sc->sc_transfer.data_rem = data_len; sc->sc_transfer.data_timeout = (data_timeout + UMASS_TIMEOUT); sc->sc_transfer.actlen = 0; sc->sc_transfer.callback = callback; sc->sc_transfer.ccb = ccb; if (sc->sc_last_xfer_index < UMASS_T_MAX) { usbd_transfer_start(sc->sc_xfer[sc->sc_last_xfer_index]); } else { ccb->ccb_h.status = CAM_TID_INVALID; xpt_done(ccb); } return; } static u_int8_t umass_bbb_get_max_lun(struct umass_softc *sc) { usb_device_request_t req; usbd_status err; u_int8_t buf = 0; /* The Get Max Lun command is a class-specific request. */ req.bmRequestType = UT_READ_CLASS_INTERFACE; req.bRequest = UR_BBB_GET_MAX_LUN; USETW(req.wValue, 0); req.wIndex[0] = sc->sc_iface_no; req.wIndex[1] = 0; USETW(req.wLength, 1); err = usbd_do_request(sc->sc_udev, &req, &buf); if (err) { buf = 0; /* Device doesn't support Get Max Lun request. */ printf("%s: Get Max Lun not supported (%s)\n", sc->sc_name, usbd_errstr(err)); } return buf; } /* * Command/Bulk/Interrupt (CBI) specific functions */ static void umass_cbi_start_status(struct umass_softc *sc) { if (sc->sc_xfer[UMASS_T_CBI_STATUS]) { umass_transfer_start(sc, UMASS_T_CBI_STATUS); } else { union ccb *ccb = sc->sc_transfer.ccb; sc->sc_transfer.ccb = NULL; sc->sc_last_xfer_index = UMASS_T_CBI_COMMAND; (sc->sc_transfer.callback) (sc, ccb, (sc->sc_transfer.data_len - sc->sc_transfer.actlen), STATUS_CMD_UNKNOWN); } return; } static void umass_t_cbi_reset1_callback(struct usbd_xfer *xfer) { struct umass_softc *sc = xfer->priv_sc; struct { usb_device_request_t req; u_int8_t buf[UMASS_CBI_DIAGNOSTIC_CMDLEN]; } UPACKED rst; u_int8_t i; USBD_CHECK_STATUS(xfer); tr_error: umass_tr_error(xfer); return; tr_transferred: umass_transfer_start(sc, UMASS_T_CBI_RESET2); return; tr_setup: /* * Command Block Reset Protocol * * First send a reset request to the device. Then clear * any possibly stalled bulk endpoints. * * This is done in 3 steps, using 3 transfers: * UMASS_T_CBI_RESET1 * UMASS_T_CBI_RESET2 * UMASS_T_CBI_RESET3 * UMASS_T_CBI_RESET4 (only if there is an interrupt endpoint) */ DPRINTF(sc, UDMASS_CBI, "CBI reset!\n"); sc->sc_reset_count ++; rst.req.bmRequestType = UT_WRITE_CLASS_INTERFACE; rst.req.bRequest = UR_CBI_ADSC; USETW(rst.req.wValue, 0); rst.req.wIndex[0] = sc->sc_iface_no; rst.req.wIndex[1] = 0; USETW(rst.req.wLength, UMASS_CBI_DIAGNOSTIC_CMDLEN); /* The 0x1d code is the SEND DIAGNOSTIC command. * To distinguish between the two, the last 10 bytes * of the CBL is filled with 0xff (section 2.2 of * the CBI specification) */ rst.req.bData[0] = 0x1d; /* Command Block Reset */ rst.req.bData[1] = 0x04; for (i = 2; i < UMASS_CBI_DIAGNOSTIC_CMDLEN; i++) { rst.req.bData[i] = 0xff; } usbd_copy_in(&(xfer->buf_data), 0, &rst, sizeof(rst)); usbd_start_hardware(xfer); return; } static void umass_t_cbi_reset2_callback(struct usbd_xfer *xfer) { umass_t_cbi_data_clear_stall_callback(xfer, UMASS_T_CBI_RESET3, UMASS_T_CBI_DATA_READ); return; } static void umass_t_cbi_reset3_callback(struct usbd_xfer *xfer) { struct umass_softc *sc = xfer->priv_sc; umass_t_cbi_data_clear_stall_callback (xfer, (sc->sc_xfer[UMASS_T_CBI_RESET4] && sc->sc_xfer[UMASS_T_CBI_STATUS]) ? UMASS_T_CBI_RESET4 : UMASS_T_CBI_COMMAND, UMASS_T_CBI_DATA_WRITE); return; } static void umass_t_cbi_reset4_callback(struct usbd_xfer *xfer) { umass_t_cbi_data_clear_stall_callback(xfer, UMASS_T_CBI_COMMAND, UMASS_T_CBI_STATUS); return; } static void umass_t_cbi_data_clear_stall_callback(struct usbd_xfer *xfer, u_int8_t next_xfer, u_int8_t stall_xfer) { struct umass_softc *sc = xfer->priv_sc; USBD_CHECK_STATUS(xfer); tr_error: umass_tr_error(xfer); return; tr_transferred: if (next_xfer == UMASS_T_CBI_STATUS) { umass_cbi_start_status(sc); } else { umass_transfer_start(sc, next_xfer); } return; tr_setup: usbd_clear_stall_tr_setup(xfer, sc->sc_xfer[stall_xfer]); usbd_clear_stall_tr_transferred(xfer, sc->sc_xfer[stall_xfer]); return; } static void umass_t_cbi_command_callback(struct usbd_xfer *xfer) { struct umass_softc *sc = xfer->priv_sc; union ccb *ccb = sc->sc_transfer.ccb; struct { usb_device_request_t req; u_int8_t buf[UMASS_MAX_CMDLEN]; } UPACKED cmd; USBD_CHECK_STATUS(xfer); tr_error: umass_tr_error(xfer); return; tr_transferred: if (sc->sc_transfer.dir == DIR_NONE) { umass_cbi_start_status(sc); } else { umass_transfer_start (sc, (sc->sc_transfer.dir == DIR_IN) ? UMASS_T_CBI_DATA_READ : UMASS_T_CBI_DATA_WRITE); } return; tr_setup: sc->sc_reset_count = 0; if (ccb) { /* * do a CBI transfer with cmd_len bytes from * cmd_data, possibly a data phase of data_len * bytes from/to the device and finally a status * read phase. */ cmd.req.bmRequestType = UT_WRITE_CLASS_INTERFACE; cmd.req.bRequest = UR_CBI_ADSC; USETW(cmd.req.wValue, 0); cmd.req.wIndex[0] = sc->sc_iface_no; cmd.req.wIndex[1] = 0; cmd.req.wLength[0] = sc->sc_transfer.cmd_len; cmd.req.wLength[1] = 0; bcopy(sc->sc_transfer.cmd_data, cmd.req.bData, sc->sc_transfer.cmd_len); xfer->length = (sizeof(cmd.req) + sc->sc_transfer.cmd_len); DIF(UDMASS_CBI, umass_cbi_dump_cmd(sc, sc->sc_transfer.cmd_data, sc->sc_transfer.cmd_len);); usbd_copy_in(&(xfer->buf_data), 0, &cmd, xfer->length); usbd_start_hardware(xfer); } return; } static void umass_t_cbi_data_read_callback(struct usbd_xfer *xfer) { struct umass_softc *sc = xfer->priv_sc; u_int32_t max_bulk = (UMASS_BULK_SIZE - (UMASS_BULK_SIZE % xfer->max_packet_size)); USBD_CHECK_STATUS(xfer); tr_error: if ((xfer->error == USBD_CANCELLED) || (sc->sc_transfer.callback != &umass_cam_cb)) { umass_tr_error(xfer); } else { umass_transfer_start(sc, UMASS_T_CBI_DATA_RD_CS); } return; tr_transferred: usbd_copy_out(&(xfer->buf_data), 0, sc->sc_transfer.data_ptr, xfer->actlen); sc->sc_transfer.data_rem -= xfer->actlen; sc->sc_transfer.data_ptr += xfer->actlen; sc->sc_transfer.actlen += xfer->actlen; if (xfer->actlen < xfer->length) { /* short transfer */ sc->sc_transfer.data_rem = 0; } tr_setup: DPRINTF(sc, UDMASS_CBI, "max_bulk=%d, data_rem=%d\n", max_bulk, sc->sc_transfer.data_rem); if (sc->sc_transfer.data_rem == 0) { umass_cbi_start_status(sc); return; } if (max_bulk > sc->sc_transfer.data_rem) { max_bulk = sc->sc_transfer.data_rem; } xfer->timeout = sc->sc_transfer.data_timeout; xfer->length = max_bulk; usbd_start_hardware(xfer); return; } static void umass_t_cbi_data_rd_cs_callback(struct usbd_xfer *xfer) { umass_t_cbi_data_clear_stall_callback(xfer, UMASS_T_CBI_STATUS, UMASS_T_CBI_DATA_READ); return; } static void umass_t_cbi_data_write_callback(struct usbd_xfer *xfer) { struct umass_softc *sc = xfer->priv_sc; u_int32_t max_bulk = (UMASS_BULK_SIZE - (UMASS_BULK_SIZE % xfer->max_packet_size)); USBD_CHECK_STATUS(xfer); tr_error: if ((xfer->error == USBD_CANCELLED) || (sc->sc_transfer.callback != &umass_cam_cb)) { umass_tr_error(xfer); } else { umass_transfer_start(sc, UMASS_T_CBI_DATA_WR_CS); } return; tr_transferred: sc->sc_transfer.data_rem -= xfer->actlen; sc->sc_transfer.data_ptr += xfer->actlen; sc->sc_transfer.actlen += xfer->actlen; tr_setup: DPRINTF(sc, UDMASS_CBI, "max_bulk=%d, data_rem=%d\n", max_bulk, sc->sc_transfer.data_rem); if (sc->sc_transfer.data_rem == 0) { umass_cbi_start_status(sc); return; } if (max_bulk > sc->sc_transfer.data_rem) { max_bulk = sc->sc_transfer.data_rem; } xfer->timeout = sc->sc_transfer.data_timeout; xfer->length = max_bulk; usbd_copy_in(&(xfer->buf_data), 0, sc->sc_transfer.data_ptr, max_bulk); usbd_start_hardware(xfer); return; } static void umass_t_cbi_data_wr_cs_callback(struct usbd_xfer *xfer) { umass_t_cbi_data_clear_stall_callback(xfer, UMASS_T_CBI_STATUS, UMASS_T_CBI_DATA_WRITE); return; } static void umass_t_cbi_status_callback(struct usbd_xfer *xfer) { struct umass_softc *sc = xfer->priv_sc; union ccb *ccb = sc->sc_transfer.ccb; u_int32_t residue; u_int8_t status; USBD_CHECK_STATUS(xfer); tr_error: DPRINTF(sc, UDMASS_CBI, "Failed to read CSW: %s\n", usbd_errstr(xfer->error)); umass_tr_error(xfer); return; tr_transferred: if (xfer->actlen < sizeof(sc->sbl)) { goto tr_setup; } usbd_copy_out(&(xfer->buf_data), 0, &(sc->sbl), sizeof(sc->sbl)); residue = (sc->sc_transfer.data_len - sc->sc_transfer.actlen); /* dissect the information in the buffer */ if (sc->sc_proto & UMASS_PROTO_UFI) { /* Section 3.4.3.1.3 specifies that the UFI command * protocol returns an ASC and ASCQ in the interrupt * data block. */ DPRINTF(sc, UDMASS_CBI, "UFI CCI, ASC = 0x%02x, " "ASCQ = 0x%02x\n", sc->sbl.ufi.asc, sc->sbl.ufi.ascq); status = (((sc->sbl.ufi.asc == 0) && (sc->sbl.ufi.ascq == 0)) ? STATUS_CMD_OK : STATUS_CMD_FAILED); sc->sc_transfer.ccb = NULL; sc->sc_last_xfer_index = UMASS_T_CBI_COMMAND; (sc->sc_transfer.callback) (sc, ccb, residue, status); return; } else { /* Command Interrupt Data Block */ DPRINTF(sc, UDMASS_CBI, "type=0x%02x, value=0x%02x\n", sc->sbl.common.type, sc->sbl.common.value); if (sc->sbl.common.type == IDB_TYPE_CCI) { status = (sc->sbl.common.value & IDB_VALUE_STATUS_MASK); status = ((status == IDB_VALUE_PASS) ? STATUS_CMD_OK : (status == IDB_VALUE_FAIL) ? STATUS_CMD_FAILED : (status == IDB_VALUE_PERSISTENT) ? STATUS_CMD_FAILED : STATUS_WIRE_FAILED); sc->sc_transfer.ccb = NULL; sc->sc_last_xfer_index = UMASS_T_CBI_COMMAND; (sc->sc_transfer.callback) (sc, ccb, residue, status); return; } } /* fallthrough */ tr_setup: usbd_start_hardware(xfer); return; } /* * CAM specific functions (used by SCSI, UFI, 8070i (ATAPI)) */ static int umass_cam_attach_sim(struct umass_softc *sc) { struct cam_devq *devq; /* Per device Queue */ /* A HBA is attached to the CAM layer. * * The CAM layer will then after a while start probing for * devices on the bus. The number of SIMs is limited to one. */ devq = cam_simq_alloc(1 /*maximum openings*/); if (devq == NULL) { return ENOMEM; } sc->sc_sim = cam_sim_alloc (&umass_cam_action, &umass_cam_poll, DEVNAME_SIM, sc /*priv*/, sc->sc_unit /*unit number*/, 1 /*maximum device openings*/, 0 /*maximum tagged device openings*/, devq); if (sc->sc_sim == NULL) { cam_simq_free(devq); return ENOMEM; } if(xpt_bus_register(sc->sc_sim, sc->sc_unit) != CAM_SUCCESS) { return ENOMEM; } return(0); } static void umass_cam_rescan_callback(struct cam_periph *periph, union ccb *ccb) { #ifdef USB_DEBUG struct umass_softc *sc = NULL; if (ccb->ccb_h.status != CAM_REQ_CMP) { DPRINTF(sc, UDMASS_SCSI, "%s:%d Rescan failed, 0x%04x\n", periph->periph_name, periph->unit_number, ccb->ccb_h.status); } else { DPRINTF(sc, UDMASS_SCSI, "%s%d: Rescan succeeded\n", periph->periph_name, periph->unit_number); } #endif xpt_free_path(ccb->ccb_h.path); free(ccb, M_USBDEV); return; } static void umass_cam_rescan(struct umass_softc *sc) { struct cam_path *path; union ccb *ccb; DPRINTF(sc, UDMASS_SCSI, "scbus%d: scanning for %d:%d:%d\n", cam_sim_path(sc->sc_sim), cam_sim_path(sc->sc_sim), sc->sc_unit, CAM_LUN_WILDCARD); ccb = malloc(sizeof(*ccb), M_USBDEV, M_WAITOK|M_ZERO); if (ccb == NULL) { return; } if (xpt_create_path(&path, xpt_periph, cam_sim_path(sc->sc_sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { free(ccb, M_USBDEV); return; } xpt_setup_ccb(&ccb->ccb_h, path, 5/*priority (low)*/); ccb->ccb_h.func_code = XPT_SCAN_BUS; ccb->ccb_h.cbfcnp = &umass_cam_rescan_callback; ccb->crcn.flags = CAM_FLAG_NONE; xpt_action(ccb); /* The scan is in progress now. */ return; } static void umass_cam_attach(struct umass_softc *sc) { #ifndef USB_DEBUG if (bootverbose) #endif printf("%s:%d:%d:%d: Attached to scbus%d\n", sc->sc_name, cam_sim_path(sc->sc_sim), sc->sc_unit, CAM_LUN_WILDCARD, cam_sim_path(sc->sc_sim)); if (!cold) { /* Notify CAM of the new device after a short delay. Any * failure is benign, as the user can still do it by hand * (camcontrol rescan ). Only do this if we are not * booting, because CAM does a scan after booting has * completed, when interrupts have been enabled. */ /* scan the new sim */ umass_cam_rescan(sc); } return; } /* umass_cam_detach * detach from the CAM layer */ static void umass_cam_detach_sim(struct umass_softc *sc) { if (sc->sc_sim) { if (xpt_bus_deregister(cam_sim_path(sc->sc_sim))) { cam_sim_free(sc->sc_sim, /*free_devq*/TRUE); } else { panic("%s: CAM layer is busy!\n", sc->sc_name); } sc->sc_sim = NULL; } return; } /* umass_cam_action * CAM requests for action come through here */ static void umass_cam_action(struct cam_sim *sim, union ccb *ccb) { struct umass_softc *sc = (struct umass_softc *)sim->softc; if (sc) { mtx_lock(&(sc->sc_mtx)); /* The softc is still there, but marked as going away. umass_cam_detach * has not yet notified CAM of the lost device however. */ if (sc->sc_last_xfer_index >= UMASS_T_MAX) { DPRINTF(sc, UDMASS_SCSI, "%d:%d:%d:func_code 0x%04x: " "Invalid target (gone)\n", cam_sim_path(sc->sc_sim), ccb->ccb_h.target_id, ccb->ccb_h.target_lun, ccb->ccb_h.func_code); ccb->ccb_h.status = CAM_TID_INVALID; xpt_done(ccb); goto done; } } /* Verify, depending on the operation to perform, that we either got a * valid sc, because an existing target was referenced, or otherwise * the SIM is addressed. * * This avoids bombing out at a printf and does give the CAM layer some * sensible feedback on errors. */ switch (ccb->ccb_h.func_code) { case XPT_SCSI_IO: case XPT_RESET_DEV: case XPT_GET_TRAN_SETTINGS: case XPT_SET_TRAN_SETTINGS: case XPT_CALC_GEOMETRY: /* the opcodes requiring a target. These should never occur. */ if (sc == NULL) { DPRINTF(sc, UDMASS_GEN, "%s:%d:%d:%d:func_code 0x%04x: " "Invalid target (target needed)\n", DEVNAME_SIM, cam_sim_path(sc->sc_sim), ccb->ccb_h.target_id, ccb->ccb_h.target_lun, ccb->ccb_h.func_code); ccb->ccb_h.status = CAM_TID_INVALID; xpt_done(ccb); goto done; } break; case XPT_PATH_INQ: case XPT_NOOP: /* The opcodes sometimes aimed at a target (sc is valid), * sometimes aimed at the SIM (sc is invalid and target is * CAM_TARGET_WILDCARD) */ if ((sc == NULL) && (ccb->ccb_h.target_id != CAM_TARGET_WILDCARD)) { DPRINTF(sc, UDMASS_SCSI, "%s:%d:%d:%d:func_code 0x%04x: " "Invalid target (no wildcard)\n", DEVNAME_SIM, cam_sim_path(sc->sc_sim), ccb->ccb_h.target_id, ccb->ccb_h.target_lun, ccb->ccb_h.func_code); ccb->ccb_h.status = CAM_TID_INVALID; xpt_done(ccb); goto done; } break; default: /* XXX Hm, we should check the input parameters */ break; } /* Perform the requested action */ switch (ccb->ccb_h.func_code) { case XPT_SCSI_IO: { u_int8_t *cmd; u_int8_t dir; if (ccb->csio.ccb_h.flags & CAM_CDB_POINTER) { cmd = (u_int8_t *)(ccb->csio.cdb_io.cdb_ptr); } else { cmd = (u_int8_t *)(ccb->csio.cdb_io.cdb_bytes); } DPRINTF(sc, UDMASS_SCSI, "%d:%d:%d:XPT_SCSI_IO: " "cmd: 0x%02x, flags: 0x%02x, " "%db cmd/%db data/%db sense\n", cam_sim_path(sc->sc_sim), ccb->ccb_h.target_id, ccb->ccb_h.target_lun, cmd[0], ccb->ccb_h.flags & CAM_DIR_MASK, ccb->csio.cdb_len, ccb->csio.dxfer_len, ccb->csio.sense_len); if (sc->sc_transfer.ccb) { DPRINTF(sc, UDMASS_SCSI, "%d:%d:%d:XPT_SCSI_IO: " "I/O in progress, deferring\n", cam_sim_path(sc->sc_sim), ccb->ccb_h.target_id, ccb->ccb_h.target_lun); ccb->ccb_h.status = CAM_SCSI_BUSY; xpt_done(ccb); goto done; } switch(ccb->ccb_h.flags & CAM_DIR_MASK) { case CAM_DIR_IN: dir = DIR_IN; break; case CAM_DIR_OUT: dir = DIR_OUT; break; default: dir = DIR_NONE; } DIF(UDMASS_SCSI, if (dir == DIR_OUT) { umass_dump_buffer(sc, ccb->csio.data_ptr, ccb->csio.dxfer_len, 48); }); ccb->ccb_h.status = CAM_REQ_INPROG | CAM_SIM_QUEUED; /* sc->sc_transform will convert the command to the command * format needed by the specific command set and return * the converted command in "sc->sc_transfer.cmd_data" */ if (sc->sc_transform(sc, cmd, ccb->csio.cdb_len)) { if (sc->sc_transfer.cmd_data[0] == INQUIRY) { /* * Handle EVPD inquiry for broken devices first * NO_INQUIRY also implies NO_INQUIRY_EVPD */ if ((sc->sc_quirks & (NO_INQUIRY_EVPD | NO_INQUIRY)) && (sc->sc_transfer.cmd_data[1] & SI_EVPD)) { struct scsi_sense_data *sense; sense = &(ccb->csio.sense_data); bzero(sense, sizeof(*sense)); sense->error_code = SSD_CURRENT_ERROR; sense->flags = SSD_KEY_ILLEGAL_REQUEST; sense->add_sense_code = 0x24; sense->extra_len = 10; ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND; ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR | CAM_AUTOSNS_VALID; xpt_done(ccb); goto done; } /* Return fake inquiry data for broken devices */ if (sc->sc_quirks & NO_INQUIRY) { memcpy(ccb->csio.data_ptr, &fake_inq_data, sizeof(fake_inq_data)); ccb->csio.scsi_status = SCSI_STATUS_OK; ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); goto done; } if (sc->sc_quirks & FORCE_SHORT_INQUIRY) { ccb->csio.dxfer_len = SHORT_INQUIRY_LENGTH; } } umass_command_start(sc, dir, ccb->csio.data_ptr, ccb->csio.dxfer_len, ccb->ccb_h.timeout, &umass_cam_cb, ccb); } else { ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); } break; } case XPT_PATH_INQ: { struct ccb_pathinq *cpi = &ccb->cpi; DPRINTF(sc, UDMASS_SCSI, "%d:%d:%d:XPT_PATH_INQ:.\n", sc ? cam_sim_path(sc->sc_sim) : -1, ccb->ccb_h.target_id, ccb->ccb_h.target_lun); /* host specific information */ cpi->version_num = 1; cpi->hba_inquiry = 0; cpi->target_sprt = 0; cpi->hba_misc = PIM_NO_6_BYTE; cpi->hba_eng_cnt = 0; cpi->max_target = UMASS_SCSIID_MAX; /* one target */ cpi->initiator_id = UMASS_SCSIID_HOST; strlcpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); strlcpy(cpi->hba_vid, "USB SCSI", HBA_IDLEN); strlcpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); cpi->unit_number = cam_sim_unit(sim); cpi->bus_id = sc->sc_unit; if (sc == NULL) { cpi->base_transfer_speed = 0; cpi->max_lun = 0; } else { if (sc->sc_quirks & FLOPPY_SPEED) { cpi->base_transfer_speed = UMASS_FLOPPY_TRANSFER_SPEED; } else if (usbd_get_speed(sc->sc_udev) == USB_SPEED_HIGH) { cpi->base_transfer_speed = UMASS_HIGH_TRANSFER_SPEED; } else { cpi->base_transfer_speed = UMASS_FULL_TRANSFER_SPEED; } cpi->max_lun = sc->sc_maxlun; } cpi->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } case XPT_RESET_DEV: { DPRINTF(sc, UDMASS_SCSI, "%d:%d:%d:XPT_RESET_DEV:.\n", cam_sim_path(sc->sc_sim), ccb->ccb_h.target_id, ccb->ccb_h.target_lun); umass_reset(sc); ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } case XPT_GET_TRAN_SETTINGS: { struct ccb_trans_settings *cts = &ccb->cts; DPRINTF(sc, UDMASS_SCSI, "%d:%d:%d:XPT_GET_TRAN_SETTINGS:.\n", cam_sim_path(sc->sc_sim), ccb->ccb_h.target_id, ccb->ccb_h.target_lun); #if (__FreeBSD_version >= 700025) cts->protocol = PROTO_SCSI; cts->protocol_version = SCSI_REV_2; cts->transport = XPORT_USB; cts->transport_version = XPORT_VERSION_UNSPECIFIED; cts->xport_specific.valid = 0; #else cts->valid = 0; cts->flags = 0; /* no disconnection, tagging */ #endif ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } case XPT_SET_TRAN_SETTINGS: { DPRINTF(sc, UDMASS_SCSI, "%d:%d:%d:XPT_SET_TRAN_SETTINGS:.\n", cam_sim_path(sc->sc_sim), ccb->ccb_h.target_id, ccb->ccb_h.target_lun); ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; xpt_done(ccb); break; } case XPT_CALC_GEOMETRY: { cam_calc_geometry(&ccb->ccg, /*extended*/1); xpt_done(ccb); break; } case XPT_NOOP: { DPRINTF(sc, UDMASS_SCSI, "%d:%d:%d:XPT_NOOP:.\n", sc ? cam_sim_path(sc->sc_sim) : -1, ccb->ccb_h.target_id, ccb->ccb_h.target_lun); ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } default: DPRINTF(sc, UDMASS_SCSI, "%d:%d:%d:func_code 0x%04x: " "Not implemented\n", sc ? cam_sim_path(sc->sc_sim) : -1, ccb->ccb_h.target_id, ccb->ccb_h.target_lun, ccb->ccb_h.func_code); ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; xpt_done(ccb); break; } done: if (sc) { mtx_unlock(&(sc->sc_mtx)); } return; } static void umass_cam_poll(struct cam_sim *sim) { struct umass_softc *sc = (struct umass_softc *) sim->softc; DPRINTF(sc, UDMASS_SCSI, "CAM poll\n"); usbd_do_poll(sc->sc_udev); return; } /* umass_cam_cb * finalise a completed CAM command */ static void umass_cam_cb(struct umass_softc *sc, union ccb *ccb, u_int32_t residue, u_int8_t status) { ccb->csio.resid = residue; switch (status) { case STATUS_CMD_OK: ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; case STATUS_CMD_UNKNOWN: case STATUS_CMD_FAILED: /* fetch sense data */ /* the rest of the command was filled in at attach */ sc->cam_scsi_sense.length = ccb->csio.sense_len; DPRINTF(sc, UDMASS_SCSI, "Fetching %d bytes of " "sense data\n", ccb->csio.sense_len); if (sc->sc_transform(sc, &(sc->cam_scsi_sense.opcode), sizeof(sc->cam_scsi_sense))) { if ((sc->sc_quirks & FORCE_SHORT_INQUIRY) && (sc->sc_transfer.cmd_data[0] == INQUIRY)) { ccb->csio.sense_len = SHORT_INQUIRY_LENGTH; } umass_command_start(sc, DIR_IN, &(ccb->csio.sense_data.error_code), ccb->csio.sense_len, ccb->ccb_h.timeout, &umass_cam_sense_cb, ccb); } else { DPRINTF(sc, UDMASS_SCSI, "SCSI_SENSE - " "no transform!\n"); ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); } break; default: /* the wire protocol failed and will have recovered * (hopefully). We return an error to CAM and let CAM retry * the command if necessary. */ ccb->ccb_h.status = CAM_REQ_CMP_ERR; xpt_done(ccb); break; } } /* * Finalise a completed autosense operation */ static void umass_cam_sense_cb(struct umass_softc *sc, union ccb *ccb, u_int32_t residue, u_int8_t status) { u_int8_t *cmd; u_int8_t key; switch (status) { case STATUS_CMD_OK: case STATUS_CMD_UNKNOWN: case STATUS_CMD_FAILED: if (ccb->csio.ccb_h.flags & CAM_CDB_POINTER) { cmd = (u_int8_t *)(ccb->csio.cdb_io.cdb_ptr); } else { cmd = (u_int8_t *)(ccb->csio.cdb_io.cdb_bytes); } key = (ccb->csio.sense_data.flags & SSD_KEY); /* Getting sense data always succeeds * (apart from wire failures): */ if ((sc->sc_quirks & RS_NO_CLEAR_UA) && (cmd[0] == INQUIRY) && (key == SSD_KEY_UNIT_ATTENTION)) { /* Ignore unit attention errors in the case where * the Unit Attention state is not cleared on * REQUEST SENSE. They will appear again at the next * command. */ ccb->ccb_h.status = CAM_REQ_CMP; } else if (key == SSD_KEY_NO_SENSE) { /* No problem after all * (in the case of CBI without CCI) */ ccb->ccb_h.status = CAM_REQ_CMP; } else if ((sc->sc_quirks & RS_NO_CLEAR_UA) && (cmd[0] == READ_CAPACITY) && (key == SSD_KEY_UNIT_ATTENTION)) { /* * Some devices do not clear the unit attention error * on request sense. We insert a test unit ready * command to make sure we clear the unit attention * condition, then allow the retry to proceed as * usual. */ ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR | CAM_AUTOSNS_VALID; ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND; #if 0 DELAY(300000); #endif DPRINTF(sc, UDMASS_SCSI, "Doing a sneaky" "TEST_UNIT_READY\n"); /* the rest of the command was filled in at attach */ if (sc->sc_transform(sc, &(sc->cam_scsi_test_unit_ready.opcode), sizeof(sc->cam_scsi_test_unit_ready))) { umass_command_start(sc, DIR_NONE, NULL, 0, ccb->ccb_h.timeout, &umass_cam_quirk_cb, ccb); } else { DPRINTF(sc, UDMASS_SCSI, "TEST_UNIT_READY - " "no transform!\n"); ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); } break; } else { ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR | CAM_AUTOSNS_VALID; ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND; } xpt_done(ccb); break; default: DPRINTF(sc, UDMASS_SCSI, "Autosense failed, " "status %d\n", status); ccb->ccb_h.status = CAM_AUTOSENSE_FAIL; xpt_done(ccb); } return; } /* * This completion code just handles the fact that we sent a test-unit-ready * after having previously failed a READ CAPACITY with CHECK_COND. Even * though this command succeeded, we have to tell CAM to retry. */ static void umass_cam_quirk_cb(struct umass_softc *sc, union ccb *ccb, u_int32_t residue, u_int8_t status) { DPRINTF(sc, UDMASS_SCSI, "Test unit ready " "returned status %d\n", status); ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR | CAM_AUTOSNS_VALID; ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND; xpt_done(ccb); return; } /* * SCSI specific functions */ static u_int8_t umass_scsi_transform(struct umass_softc *sc, u_int8_t *cmd_ptr, u_int8_t cmd_len) { if ((cmd_len == 0) || (cmd_len > sizeof(sc->sc_transfer.cmd_data))) { DPRINTF(sc, UDMASS_SCSI, "Invalid command " "length: %d bytes\n", cmd_len); return 0; /* failure */ } sc->sc_transfer.cmd_len = cmd_len; switch (cmd_ptr[0]) { case TEST_UNIT_READY: if (sc->sc_quirks & NO_TEST_UNIT_READY) { DPRINTF(sc, UDMASS_SCSI, "Converted TEST_UNIT_READY " "to START_UNIT\n"); bzero(sc->sc_transfer.cmd_data, cmd_len); sc->sc_transfer.cmd_data[0] = START_STOP_UNIT; sc->sc_transfer.cmd_data[4] = SSS_START; return 1; } break; case INQUIRY: /* some drives wedge when asked for full inquiry information. */ if (sc->sc_quirks & FORCE_SHORT_INQUIRY) { bcopy(cmd_ptr, sc->sc_transfer.cmd_data, cmd_len); sc->sc_transfer.cmd_data[4] = SHORT_INQUIRY_LENGTH; return 1; } break; } bcopy(cmd_ptr, sc->sc_transfer.cmd_data, cmd_len); return 1; } static u_int8_t umass_rbc_transform(struct umass_softc *sc, u_int8_t *cmd_ptr, u_int8_t cmd_len) { if ((cmd_len == 0) || (cmd_len > sizeof(sc->sc_transfer.cmd_data))) { DPRINTF(sc, UDMASS_SCSI, "Invalid command " "length: %d bytes\n", cmd_len); return 0; /* failure */ } switch (cmd_ptr[0]) { /* these commands are defined in RBC: */ case READ_10: case READ_CAPACITY: case START_STOP_UNIT: case SYNCHRONIZE_CACHE: case WRITE_10: case 0x2f: /* VERIFY_10 is absent from scsi_all.h??? */ case INQUIRY: case MODE_SELECT_10: case MODE_SENSE_10: case TEST_UNIT_READY: case WRITE_BUFFER: /* The following commands are not listed in my copy of the RBC specs. * CAM however seems to want those, and at least the Sony DSC device * appears to support those as well */ case REQUEST_SENSE: case PREVENT_ALLOW: bcopy(cmd_ptr, sc->sc_transfer.cmd_data, cmd_len); if ((sc->sc_quirks & RBC_PAD_TO_12) && (cmd_len < 12)) { bzero(sc->sc_transfer.cmd_data + cmd_len, 12 - cmd_len); cmd_len = 12; } sc->sc_transfer.cmd_len = cmd_len; return 1; /* sucess */ /* All other commands are not legal in RBC */ default: DPRINTF(sc, UDMASS_SCSI, "Unsupported RBC " "command 0x%02x\n", cmd_ptr[0]); return 0; /* failure */ } } static u_int8_t umass_ufi_transform(struct umass_softc *sc, u_int8_t *cmd_ptr, u_int8_t cmd_len) { if ((cmd_len == 0) || (cmd_len > sizeof(sc->sc_transfer.cmd_data))) { DPRINTF(sc, UDMASS_SCSI, "Invalid command " "length: %d bytes\n", cmd_len); return 0; /* failure */ } /* An UFI command is always 12 bytes in length */ sc->sc_transfer.cmd_len = UFI_COMMAND_LENGTH; /* Zero the command data */ bzero(sc->sc_transfer.cmd_data, UFI_COMMAND_LENGTH); switch (cmd_ptr[0]) { /* Commands of which the format has been verified. They should work. * Copy the command into the (zeroed out) destination buffer. */ case TEST_UNIT_READY: if (sc->sc_quirks & NO_TEST_UNIT_READY) { /* Some devices do not support this command. * Start Stop Unit should give the same results */ DPRINTF(sc, UDMASS_UFI, "Converted TEST_UNIT_READY " "to START_UNIT\n"); sc->sc_transfer.cmd_data[0] = START_STOP_UNIT; sc->sc_transfer.cmd_data[4] = SSS_START; return 1; } break; case REZERO_UNIT: case REQUEST_SENSE: case FORMAT_UNIT: case INQUIRY: case START_STOP_UNIT: case SEND_DIAGNOSTIC: case PREVENT_ALLOW: case READ_CAPACITY: case READ_10: case WRITE_10: case POSITION_TO_ELEMENT: /* SEEK_10 */ case WRITE_AND_VERIFY: case VERIFY: case MODE_SELECT_10: case MODE_SENSE_10: case READ_12: case WRITE_12: case READ_FORMAT_CAPACITIES: break; default: DPRINTF(sc, UDMASS_SCSI, "Unsupported UFI " "command 0x%02x\n", cmd_ptr[0]); return 0; /* failure */ } bcopy(cmd_ptr, sc->sc_transfer.cmd_data, cmd_len); return 1; /* success */ } /* * 8070i (ATAPI) specific functions */ static u_int8_t umass_atapi_transform(struct umass_softc *sc, u_int8_t *cmd_ptr, u_int8_t cmd_len) { if ((cmd_len == 0) || (cmd_len > sizeof(sc->sc_transfer.cmd_data))) { DPRINTF(sc, UDMASS_SCSI, "Invalid command " "length: %d bytes\n", cmd_len); return 0; /* failure */ } /* An ATAPI command is always 12 bytes in length. */ sc->sc_transfer.cmd_len = ATAPI_COMMAND_LENGTH; /* Zero the command data */ bzero(sc->sc_transfer.cmd_data, ATAPI_COMMAND_LENGTH); switch (cmd_ptr[0]) { /* Commands of which the format has been verified. They should work. * Copy the command into the destination buffer. */ case INQUIRY: /* some drives wedge when asked for full inquiry information. */ if (sc->sc_quirks & FORCE_SHORT_INQUIRY) { bcopy(cmd_ptr, sc->sc_transfer.cmd_data, cmd_len); sc->sc_transfer.cmd_data[4] = SHORT_INQUIRY_LENGTH; return 1; } break; case TEST_UNIT_READY: if (sc->sc_quirks & NO_TEST_UNIT_READY) { DPRINTF(sc, UDMASS_SCSI, "Converted TEST_UNIT_READY " "to START_UNIT\n"); sc->sc_transfer.cmd_data[0] = START_STOP_UNIT; sc->sc_transfer.cmd_data[4] = SSS_START; return 1; } break; case REZERO_UNIT: case REQUEST_SENSE: case START_STOP_UNIT: case SEND_DIAGNOSTIC: case PREVENT_ALLOW: case READ_CAPACITY: case READ_10: case WRITE_10: case POSITION_TO_ELEMENT: /* SEEK_10 */ case SYNCHRONIZE_CACHE: case MODE_SELECT_10: case MODE_SENSE_10: case READ_BUFFER: case 0x42: /* READ_SUBCHANNEL */ case 0x43: /* READ_TOC */ case 0x44: /* READ_HEADER */ case 0x47: /* PLAY_MSF (Play Minute/Second/Frame) */ case 0x48: /* PLAY_TRACK */ case 0x49: /* PLAY_TRACK_REL */ case 0x4b: /* PAUSE */ case 0x51: /* READ_DISK_INFO */ case 0x52: /* READ_TRACK_INFO */ case 0x54: /* SEND_OPC */ case 0x59: /* READ_MASTER_CUE */ case 0x5b: /* CLOSE_TR_SESSION */ case 0x5c: /* READ_BUFFER_CAP */ case 0x5d: /* SEND_CUE_SHEET */ case 0xa1: /* BLANK */ case 0xa5: /* PLAY_12 */ case 0xa6: /* EXCHANGE_MEDIUM */ case 0xad: /* READ_DVD_STRUCTURE */ case 0xbb: /* SET_CD_SPEED */ case 0xe5: /* READ_TRACK_INFO_PHILIPS */ break;; case READ_12: case WRITE_12: default: DPRINTF(sc, UDMASS_SCSI, "Unsupported ATAPI " "command 0x%02x - trying anyway\n", cmd_ptr[0]); break;; } bcopy(cmd_ptr, sc->sc_transfer.cmd_data, cmd_len); return 1; /* success */ } static u_int8_t umass_no_transform(struct umass_softc *sc, u_int8_t *cmd, u_int8_t cmdlen) { return 0; /* failure */ } #ifdef USB_DEBUG static void umass_bbb_dump_cbw(struct umass_softc *sc, umass_bbb_cbw_t *cbw) { u_int8_t *c = cbw->CBWCDB; u_int32_t dlen = UGETDW(cbw->dCBWDataTransferLength); u_int32_t tag = UGETDW(cbw->dCBWTag); u_int8_t clen = cbw->bCDBLength; u_int8_t flags = cbw->bCBWFlags; u_int8_t lun = cbw->bCBWLUN; DPRINTF(sc, UDMASS_BBB, "CBW %d: cmd = %db " "(0x%02x%02x%02x%02x%02x%02x%s), " "data = %db, lun = %d, dir = %s\n", tag, clen, c[0], c[1], c[2], c[3], c[4], c[5], (clen > 6? "...":""), dlen, lun, (flags == CBWFLAGS_IN? "in": (flags == CBWFLAGS_OUT? "out":""))); return; } static void umass_bbb_dump_csw(struct umass_softc *sc, umass_bbb_csw_t *csw) { u_int32_t sig = UGETDW(csw->dCSWSignature); u_int16_t tag = UGETW(csw->dCSWTag); u_int32_t res = UGETDW(csw->dCSWDataResidue); u_int8_t status = csw->bCSWStatus; DPRINTF(sc, UDMASS_BBB, "CSW %d: sig = 0x%08x (%s), tag = %d, " "res = %d, status = 0x%02x (%s)\n", tag, sig, (sig == CSWSIGNATURE? "valid":"invalid"), tag, res, status, (status == CSWSTATUS_GOOD? "good": (status == CSWSTATUS_FAILED? "failed": (status == CSWSTATUS_PHASE? "phase":"")))); return; } static void umass_cbi_dump_cmd(struct umass_softc *sc, void *cmd, u_int8_t cmdlen) { u_int8_t *c = cmd; u_int8_t dir = sc->sc_transfer.dir; DPRINTF(sc, UDMASS_BBB, "cmd = %db " "(0x%02x%02x%02x%02x%02x%02x%s), " "data = %db, dir = %s\n", cmdlen, c[0], c[1], c[2], c[3], c[4], c[5], (cmdlen > 6? "...":""), sc->sc_transfer.data_len, (dir == DIR_IN? "in": (dir == DIR_OUT? "out": (dir == DIR_NONE? "no data phase": "")))); return; } static void umass_dump_buffer(struct umass_softc *sc, u_int8_t *buffer, u_int32_t buflen, u_int32_t printlen) { u_int32_t i, j; char s1[40]; char s2[40]; char s3[5]; s1[0] = '\0'; s3[0] = '\0'; sprintf(s2, " buffer=%p, buflen=%d", buffer, buflen); for (i = 0; (i < buflen) && (i < printlen); i++) { j = i % 16; if (j == 0 && i != 0) { DPRINTF(sc, UDMASS_GEN, "0x %s%s\n", s1, s2); s2[0] = '\0'; } sprintf(&s1[j*2], "%02x", buffer[i] & 0xff); } if (buflen > printlen) sprintf(s3, " ..."); DPRINTF(sc, UDMASS_GEN, "0x %s%s%s\n", s1, s2, s3); return; } #endif pwcbsd/usb/umct.c000644 000423 000000 00000050513 10551670754 014521 0ustar00luigiwheel000000 000000 /*- * Copyright (c) 2003 Scott Long * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ /* * Driver for the MCT (Magic Control Technology) USB-RS232 Converter. * Based on the superb documentation from the linux mct_u232 driver by * Wolfgang Grandeggar . * This device smells a lot like the Belkin F5U103, except that it has * suffered some mild brain-damage. This driver is based off of the ubsa.c * driver from Alexander Kabaev . Merging the two together * might be useful, though the subtle differences might lead to lots of * #ifdef's. */ /* * NOTE: all function names beginning like "umct_cfg_" can only * be called from within the config thread function ! */ #include #include #include #include #include #include #include #define usbd_config_td_cc umct_config_copy #define usbd_config_td_softc umct_softc #include #include #include #include #include "usbdevs.h" __FBSDID("$FreeBSD: src/sys/dev/usb/umct.c,v 1.10 2006/02/12 17:32:59 glebius Exp $"); /* The UMCT advertises the standard 8250 UART registers */ #define UMCT_GET_MSR 2 /* Get Modem Status Register */ #define UMCT_GET_MSR_SIZE 1 #define UMCT_GET_LCR 6 /* Get Line Control Register */ #define UMCT_GET_LCR_SIZE 1 #define UMCT_SET_BAUD 5 /* Set the Baud Rate Divisor */ #define UMCT_SET_BAUD_SIZE 4 #define UMCT_SET_LCR 7 /* Set Line Control Register */ #define UMCT_SET_LCR_SIZE 1 #define UMCT_SET_MCR 10 /* Set Modem Control Register */ #define UMCT_SET_MCR_SIZE 1 #define UMCT_INTR_INTERVAL 100 #define UMCT_IFACE_INDEX 0 #define UMCT_CONFIG_INDEX 1 #define UMCT_ENDPT_MAX 6 /* units */ #define DPRINTF(...) struct umct_softc { struct ucom_softc sc_ucom; struct usbd_config_td sc_config_td; struct usbd_device * sc_udev; struct usbd_xfer * sc_xfer[UMCT_ENDPT_MAX]; device_t sc_dev; u_int32_t sc_unit; u_int32_t sc_baud; u_int32_t sc_cflag; u_int16_t sc_obufsize; u_int8_t sc_break_onoff; u_int8_t sc_dtr_onoff; u_int8_t sc_rts_onoff; u_int8_t sc_lsr; u_int8_t sc_msr; u_int8_t sc_lcr; u_int8_t sc_mcr; u_int8_t sc_name[16]; u_int8_t sc_flags; #define UMCT_FLAG_READ_STALL 0x01 #define UMCT_FLAG_WRITE_STALL 0x02 #define UMCT_FLAG_INTR_STALL 0x04 u_int8_t sc_iface_no; }; struct umct_config_copy { u_int32_t baud; u_int32_t cflag; u_int8_t dtr_onoff; u_int8_t rts_onoff; u_int8_t break_onoff; }; static device_probe_t umct_probe; static device_attach_t umct_attach; static device_detach_t umct_detach; static void umct_config_copy(struct umct_softc *sc, struct umct_config_copy *cc, u_int16_t refcount); static void umct_cfg_do_request(struct umct_softc *sc, u_int8_t request, u_int16_t len, u_int32_t value); static void umct_intr_clear_stall_callback(struct usbd_xfer *xfer); static void umct_intr_callback(struct usbd_xfer *xfer); static void umct_get_status(struct ucom_softc *ucom, u_int8_t *lsr, u_int8_t *msr); static void umct_set_break(struct ucom_softc *ucom, u_int8_t onoff); static void umct_cfg_set_break(struct umct_softc *sc, struct umct_config_copy *cc, u_int16_t refcount); static void umct_set_dtr(struct ucom_softc *ucom, u_int8_t onoff); static void umct_cfg_set_dtr(struct umct_softc *sc, struct umct_config_copy *cc, u_int16_t refcount); static void umct_set_rts(struct ucom_softc *ucom, u_int8_t onoff); static void umct_cfg_set_rts(struct umct_softc *sc, struct umct_config_copy *cc, u_int16_t refcount); static u_int8_t umct_calc_baud(u_int32_t baud); static int umct_param(struct ucom_softc *ucom, struct termios *ti); static void umct_cfg_param(struct umct_softc *sc, struct umct_config_copy *cc, u_int16_t refcount); static int umct_open(struct ucom_softc *ucom); static void umct_close(struct ucom_softc *ucom); static void umct_start_read(struct ucom_softc *ucom); static void umct_stop_read(struct ucom_softc *ucom); static void umct_start_write(struct ucom_softc *ucom); static void umct_stop_write(struct ucom_softc *ucom); static void umct_write_callback(struct usbd_xfer *xfer); static void umct_write_clear_stall_callback(struct usbd_xfer *xfer); static void umct_read_callback(struct usbd_xfer *xfer); static void umct_read_clear_stall_callback(struct usbd_xfer *xfer); static const struct usbd_config umct_config[UMCT_ENDPT_MAX] = { [0] = { .type = UE_BULK, .endpoint = -1, /* any */ .direction = UE_DIR_OUT, .bufsize = 0, /* use wMaxPacketSize */ .flags = 0, .callback = &umct_write_callback, }, [1] = { .type = UE_INTERRUPT, .endpoint = -1, /* any */ .direction = UE_DIR_IN, .flags = USBD_SHORT_XFER_OK, .bufsize = 0, /* use wMaxPacketSize */ .callback = &umct_read_callback, .index = 0, /* first interrupt endpoint */ }, [2] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .flags = (USBD_USE_DMA), .callback = &umct_write_clear_stall_callback, .timeout = 1000, /* 1 second */ }, [3] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .flags = (USBD_USE_DMA), .callback = &umct_read_clear_stall_callback, .timeout = 1000, /* 1 second */ }, [4] = { .type = UE_INTERRUPT, .endpoint = -1, /* any */ .direction = UE_DIR_IN, .flags = USBD_SHORT_XFER_OK, .bufsize = 0, /* use wMaxPacketSize */ .callback = &umct_intr_callback, .index = 1, /* second interrupt endpoint */ }, [5] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .flags = (USBD_USE_DMA), .callback = &umct_intr_clear_stall_callback, .timeout = 1000, /* 1 second */ }, }; static const struct ucom_callback umct_callback = { .ucom_get_status = &umct_get_status, .ucom_set_dtr = &umct_set_dtr, .ucom_set_rts = &umct_set_rts, .ucom_set_break = &umct_set_break, .ucom_param = &umct_param, .ucom_open = &umct_open, .ucom_close = &umct_close, .ucom_start_read = &umct_start_read, .ucom_stop_read = &umct_stop_read, .ucom_start_write = &umct_start_write, .ucom_stop_write = &umct_stop_write, }; static const struct umct_product { uint16_t vendor; uint16_t product; } umct_products[] = { { USB_VENDOR_MCT, USB_PRODUCT_MCT_USB232 }, { USB_VENDOR_MCT, USB_PRODUCT_MCT_SITECOM_USB232 }, { USB_VENDOR_MCT, USB_PRODUCT_MCT_DU_H3SP_USB232 }, { USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5U109 }, { USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5U409 }, { 0, 0 } }; static device_method_t umct_methods[] = { DEVMETHOD(device_probe, umct_probe), DEVMETHOD(device_attach, umct_attach), DEVMETHOD(device_detach, umct_detach), { 0, 0 } }; static devclass_t umct_devclass; static driver_t umct_driver = { .name = "umct", .methods = umct_methods, .size = sizeof(struct umct_softc), }; DRIVER_MODULE(umct, uhub, umct_driver, umct_devclass, usbd_driver_load, 0); MODULE_DEPEND(umct, usb, 1, 1, 1); MODULE_DEPEND(umct, ucom, UCOM_MINVER, UCOM_PREFVER, UCOM_MAXVER); MODULE_VERSION(umct, 1); static int umct_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); u_int32_t i; if (uaa->iface != NULL) { return UMATCH_NONE; } for (i = 0; umct_products[i].vendor != 0; i++) { if ((umct_products[i].vendor == uaa->vendor) && (umct_products[i].product == uaa->product)) { return UMATCH_VENDOR_PRODUCT; } } return UMATCH_NONE; } static int umct_attach(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct umct_softc *sc = device_get_softc(dev); struct usbd_interface *iface; usb_interface_descriptor_t *id; int32_t error; if (sc == NULL) { return ENOMEM; } sc->sc_udev = uaa->device; sc->sc_dev = dev; sc->sc_unit = device_get_unit(dev); usbd_set_desc(dev, uaa->device); snprintf(sc->sc_name, sizeof(sc->sc_name), "%s", device_get_nameunit(dev)); error = usbd_set_config_index(uaa->device, UMCT_CONFIG_INDEX, 1); if (error) { device_printf(dev, "failed to set configuration: " "%s\n", usbd_errstr(error)); goto detach; } iface = usbd_get_iface(uaa->device, UMCT_IFACE_INDEX); if (iface == NULL) { device_printf(dev, "no interface!\n"); goto detach; } id = usbd_get_interface_descriptor(iface); if (id == NULL) { device_printf(dev, "no interface descriptor!\n"); goto detach; } sc->sc_iface_no = id->bInterfaceNumber; error = usbd_transfer_setup(uaa->device, UMCT_IFACE_INDEX, sc->sc_xfer, umct_config, UMCT_ENDPT_MAX, sc, &Giant); if (error) { device_printf(dev, "allocating USB " "transfers failed!\n"); goto detach; } /* * The real bulk-in endpoint is also marked as an interrupt. * The only way to differentiate it from the real interrupt * endpoint is to look at the wMaxPacketSize field. */ if (sc->sc_xfer[1]->max_packet_size == 0x2) { /* guessed wrong - switch around endpoints */ struct usbd_xfer *temp = sc->sc_xfer[4]; sc->sc_xfer[4] = sc->sc_xfer[1]; sc->sc_xfer[1] = temp; sc->sc_xfer[1]->callback = &umct_read_callback; sc->sc_xfer[4]->callback = &umct_intr_callback; } sc->sc_obufsize = sc->sc_xfer[0]->length; if (uaa->product == USB_PRODUCT_MCT_SITECOM_USB232) { if (sc->sc_obufsize > 16) { sc->sc_obufsize = 16; } } error = usbd_config_td_setup(&(sc->sc_config_td), sc, &Giant, &umct_config_copy, NULL, sizeof(struct umct_config_copy), 16); if (error) { device_printf(dev, "could not setup config " "thread!\n"); goto detach; } error = ucom_attach(&(sc->sc_ucom), 1, sc, &umct_callback, &Giant); if (error) { goto detach; } return 0; /* success */ detach: umct_detach(dev); return ENXIO; /* failure */ } static int umct_detach(device_t dev) { struct umct_softc *sc = device_get_softc(dev); mtx_lock(&Giant); usbd_config_td_stop(&(sc->sc_config_td)); mtx_unlock(&Giant); ucom_detach(&(sc->sc_ucom), 1); usbd_transfer_unsetup(sc->sc_xfer, UMCT_ENDPT_MAX); usbd_config_td_unsetup(&(sc->sc_config_td)); return 0; } static void umct_config_copy(struct umct_softc *sc, struct umct_config_copy *cc, u_int16_t refcount) { cc->baud = sc->sc_baud; cc->cflag = sc->sc_cflag; cc->dtr_onoff = sc->sc_dtr_onoff; cc->rts_onoff = sc->sc_rts_onoff; cc->break_onoff = sc->sc_break_onoff; return; } static void umct_cfg_do_request(struct umct_softc *sc, u_int8_t request, u_int16_t len, u_int32_t value) { usb_device_request_t req; usbd_status err; u_int8_t temp[4]; if (usbd_config_td_is_gone(&(sc->sc_config_td))) { goto done; } req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = request; USETW(req.wValue, 0); USETW(req.wIndex, sc->sc_iface_no); USETW(req.wLength, len); USETDW(temp, value); err = usbd_do_request_flags_mtx(sc->sc_udev, &Giant, &req, temp, 0, NULL, 1000); if (err) { DPRINTF(sc, 0, "device request failed, err=%s " "(ignored)\n", usbd_errstr(err)); } done: return; } static void umct_intr_clear_stall_callback(struct usbd_xfer *xfer) { struct umct_softc *sc = xfer->priv_sc; struct usbd_xfer *xfer_other = sc->sc_xfer[4]; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, xfer_other); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, xfer_other); sc->sc_flags &= ~UMCT_FLAG_INTR_STALL; usbd_transfer_start(xfer_other); return; tr_error: /* bomb out */ sc->sc_flags &= ~UMCT_FLAG_INTR_STALL; DPRINTF(sc, 0, "interrupt read pipe stopped\n"); return; } static void umct_intr_callback(struct usbd_xfer *xfer) { struct umct_softc *sc = xfer->priv_sc; u_int8_t *ptr = xfer->buffer; USBD_CHECK_STATUS(xfer); tr_error: if (xfer->error != USBD_CANCELLED) { /* start clear stall */ sc->sc_flags |= UMCT_FLAG_INTR_STALL; usbd_transfer_start(sc->sc_xfer[5]); } return; tr_transferred: if (xfer->actlen < 2) { DPRINTF(sc, 0, "too short message\n"); goto tr_setup; } sc->sc_msr = ptr[0]; sc->sc_lsr = ptr[1]; ucom_status_change(&(sc->sc_ucom)); tr_setup: if (sc->sc_flags & UMCT_FLAG_INTR_STALL) { usbd_transfer_start(sc->sc_xfer[5]); } else { usbd_start_hardware(xfer); } return; } static void umct_get_status(struct ucom_softc *ucom, u_int8_t *lsr, u_int8_t *msr) { struct umct_softc *sc = ucom->sc_parent; if (lsr != NULL) { *lsr = sc->sc_lsr; } if (msr != NULL) { *msr = sc->sc_msr; } return; } static void umct_set_break(struct ucom_softc *ucom, u_int8_t onoff) { struct umct_softc *sc = ucom->sc_parent; sc->sc_break_onoff = onoff; usbd_config_td_queue_command (&(sc->sc_config_td), &umct_cfg_set_break, 0); return; } static void umct_cfg_set_break(struct umct_softc *sc, struct umct_config_copy *cc, u_int16_t refcount) { if (cc == NULL) { /* nothing to do */ return; } sc->sc_lcr &= ~0x40; sc->sc_lcr |= (cc->break_onoff) ? 0x40 : 0; umct_cfg_do_request(sc, UMCT_SET_LCR, UMCT_SET_LCR_SIZE, sc->sc_lcr); return; } static void umct_set_dtr(struct ucom_softc *ucom, u_int8_t onoff) { struct umct_softc *sc = ucom->sc_parent; sc->sc_dtr_onoff = onoff; usbd_config_td_queue_command (&(sc->sc_config_td), &umct_cfg_set_dtr, 0); return; } static void umct_cfg_set_dtr(struct umct_softc *sc, struct umct_config_copy *cc, u_int16_t refcount) { if (cc == NULL) { /* nothing to do */ return; } sc->sc_mcr &= ~0x01; sc->sc_mcr |= (cc->dtr_onoff) ? 0x01 : 0; umct_cfg_do_request(sc, UMCT_SET_MCR, UMCT_SET_MCR_SIZE, sc->sc_mcr); return; } static void umct_set_rts(struct ucom_softc *ucom, u_int8_t onoff) { struct umct_softc *sc = ucom->sc_parent; sc->sc_rts_onoff = onoff; usbd_config_td_queue_command (&(sc->sc_config_td), &umct_cfg_set_rts, 0); return; } static void umct_cfg_set_rts(struct umct_softc *sc, struct umct_config_copy *cc, u_int16_t refcount) { if (cc == NULL) { /* nothing to do */ return; } sc->sc_mcr &= ~0x2; sc->sc_mcr |= (cc->rts_onoff) ? 0x02 : 0; umct_cfg_do_request(sc, UMCT_SET_MCR, UMCT_SET_MCR_SIZE, sc->sc_mcr); return; } static u_int8_t umct_calc_baud(u_int32_t baud) { switch(baud) { case B300: return (0x1); case B600: return (0x2); case B1200: return (0x3); case B2400: return (0x4); case B4800: return (0x6); case B9600: return (0x8); case B19200: return (0x9); case B38400: return (0xa); case B57600: return (0xb); case 115200: return (0xc); case B0: default: break; } return (0x0); } static int umct_param(struct ucom_softc *ucom, struct termios *ti) { struct umct_softc *sc = ucom->sc_parent; sc->sc_baud = ti->c_ospeed; sc->sc_cflag = ti->c_cflag; usbd_config_td_queue_command (&(sc->sc_config_td), &umct_cfg_param, 0); return 0; } static void umct_cfg_param(struct umct_softc *sc, struct umct_config_copy *cc, u_int16_t refcount) { u_int32_t value; if (cc == NULL) { /* nothing to do */ return; } value = umct_calc_baud(cc->baud); umct_cfg_do_request(sc, UMCT_SET_BAUD, UMCT_SET_BAUD_SIZE, value); value = (sc->sc_lcr & 0x40); switch (cc->cflag & CSIZE) { case CS5: value |= 0x0; break; case CS6: value |= 0x1; break; case CS7: value |= 0x2; break; case CS8: value |= 0x3; break; default: value |= 0x0; break; } value |= (cc->cflag & CSTOPB) ? 0x4 : 0; if (cc->cflag & PARENB) { value |= 0x8; value |= (cc->cflag & PARODD) ? 0x0 : 0x10; } /* * XXX There doesn't seem to be a way to tell the device * to use flow control. */ sc->sc_lcr = value; umct_cfg_do_request(sc, UMCT_SET_LCR, UMCT_SET_LCR_SIZE, value); return; } static int umct_open(struct ucom_softc *ucom) { struct umct_softc *sc = ucom->sc_parent; usbd_transfer_start(sc->sc_xfer[4]); return 0; } static void umct_close(struct ucom_softc *ucom) { struct umct_softc *sc = ucom->sc_parent; usbd_transfer_stop(sc->sc_xfer[5]); usbd_transfer_stop(sc->sc_xfer[4]); return; } static void umct_start_read(struct ucom_softc *ucom) { struct umct_softc *sc = ucom->sc_parent; usbd_transfer_start(sc->sc_xfer[1]); return; } static void umct_stop_read(struct ucom_softc *ucom) { struct umct_softc *sc = ucom->sc_parent; usbd_transfer_stop(sc->sc_xfer[3]); usbd_transfer_stop(sc->sc_xfer[1]); return; } static void umct_start_write(struct ucom_softc *ucom) { struct umct_softc *sc = ucom->sc_parent; usbd_transfer_start(sc->sc_xfer[0]); return; } static void umct_stop_write(struct ucom_softc *ucom) { struct umct_softc *sc = ucom->sc_parent; usbd_transfer_stop(sc->sc_xfer[2]); usbd_transfer_stop(sc->sc_xfer[0]); return; } static void umct_write_callback(struct usbd_xfer *xfer) { struct umct_softc *sc = xfer->priv_sc; u_int32_t actlen; USBD_CHECK_STATUS(xfer); tr_error: if (xfer->error != USBD_CANCELLED) { sc->sc_flags |= UMCT_FLAG_WRITE_STALL; usbd_transfer_start(sc->sc_xfer[2]); } return; tr_setup: tr_transferred: if (sc->sc_flags & UMCT_FLAG_WRITE_STALL) { usbd_transfer_start(sc->sc_xfer[2]); return; } if(ucom_get_data(&(sc->sc_ucom), xfer->buffer, sc->sc_obufsize, &actlen)) { xfer->length = actlen; usbd_start_hardware(xfer); } return; } static void umct_write_clear_stall_callback(struct usbd_xfer *xfer) { struct umct_softc *sc = xfer->priv_sc; struct usbd_xfer *xfer_other = sc->sc_xfer[0]; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, xfer_other); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, xfer_other); sc->sc_flags &= ~UMCT_FLAG_WRITE_STALL; usbd_transfer_start(xfer_other); return; tr_error: sc->sc_flags &= ~UMCT_FLAG_WRITE_STALL; DPRINTF(sc, 0, "clear stall failed, error=%s\n", usbd_errstr(xfer->error)); return; } static void umct_read_callback(struct usbd_xfer *xfer) { struct umct_softc *sc = xfer->priv_sc; u_int8_t *ptr = xfer->buffer; USBD_CHECK_STATUS(xfer); tr_error: if (xfer->error != USBD_CANCELLED) { sc->sc_flags |= UMCT_FLAG_READ_STALL; usbd_transfer_start(sc->sc_xfer[3]); } return; tr_transferred: if (xfer->actlen) { ucom_put_data(&(sc->sc_ucom), ptr, xfer->actlen); } tr_setup: if (sc->sc_flags & UMCT_FLAG_READ_STALL) { usbd_transfer_start(sc->sc_xfer[3]); } else { usbd_start_hardware(xfer); } return; } static void umct_read_clear_stall_callback(struct usbd_xfer *xfer) { struct umct_softc *sc = xfer->priv_sc; struct usbd_xfer *xfer_other = sc->sc_xfer[1]; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, xfer_other); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, xfer_other); sc->sc_flags &= ~UMCT_FLAG_READ_STALL; usbd_transfer_start(xfer_other); return; tr_error: sc->sc_flags &= ~UMCT_FLAG_READ_STALL; DPRINTF(sc, 0, "clear stall failed, error=%s\n", usbd_errstr(xfer->error)); return; } pwcbsd/usb/umodem.c000644 000423 000000 00000065560 10551670754 015047 0ustar00luigiwheel000000 000000 /* $NetBSD: umodem.c,v 1.45 2002/09/23 05:51:23 simonb Exp $ */ /*- * Copyright (c) 2003, M. Warner Losh . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * Comm Class spec: http://www.usb.org/developers/devclass_docs/usbccs10.pdf * http://www.usb.org/developers/devclass_docs/usbcdc11.pdf */ /* * TODO: * - Add error recovery in various places; the big problem is what * to do in a callback if there is an error. * - Implement a Call Device for modems without multiplexed commands. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" __FBSDID("$FreeBSD: src/sys/dev/usb/umodem.c $"); #ifdef USB_DEBUG #define DPRINTF(n,fmt,...) \ do { if (umodem_debug > (n)) { \ printf("%s: " fmt, __FUNCTION__,## __VA_ARGS__); } } while (0) static int umodem_debug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, umodem, CTLFLAG_RW, 0, "USB umodem"); SYSCTL_INT(_hw_usb_umodem, OID_AUTO, debug, CTLFLAG_RW, &umodem_debug, 0, "umodem debug level"); #else #define DPRINTF(...) #endif static const struct umodem_product { u_int16_t vendor; u_int16_t product; u_int8_t interface; } umodem_products[] = { /* Kyocera AH-K3001V*/ { USB_VENDOR_KYOCERA, USB_PRODUCT_KYOCERA_AHK3001V, 0 }, { 0, 0, 0 }, }; /* * These are the maximum number of bytes transferred per frame. * If some really high speed devices should use this driver they * may need to be increased, but this is good enough for normal modems. */ #define UMODEM_IBUFSIZE 64 #define UMODEM_OBUFSIZE 256 #define UMODEM_N_DATA_TRANSFER 7 #define UMODEM_N_INTR_TRANSFER 2 #define UMODEM_MODVER 1 /* module version */ struct umodem_softc { struct ucom_softc sc_ucom; usb_cdc_line_state_t sc_line_state; /* current line state */ struct usbd_xfer * sc_xfer_data[UMODEM_N_DATA_TRANSFER]; struct usbd_xfer * sc_xfer_intr[UMODEM_N_INTR_TRANSFER]; u_int8_t sc_lsr; /* local status register */ u_int8_t sc_msr; /* modem status register */ u_int8_t sc_break; /* current BREAK state */ u_int8_t sc_dtr; /* current DTR state */ u_int8_t sc_rts; /* current RTS state */ u_int8_t sc_ctrl_iface_no; u_int8_t sc_ctrl_iface_index; u_int8_t sc_data_iface_no; u_int8_t sc_data_iface_index; u_int8_t sc_cm_over_data; u_int8_t sc_cm_cap; /* CM capabilities */ u_int8_t sc_acm_cap; /* ACM capabilities */ u_int8_t sc_flag; #define UMODEM_FLAG_READ_STALL 0x01 #define UMODEM_FLAG_WRITE_STALL 0x02 #define UMODEM_FLAG_INTR_STALL 0x04 #define UMODEM_FLAG_SET_LS 0x08 #define UMODEM_FLAG_SEND_BREAK 0x10 #define UMODEM_FLAG_SET_LC 0x20 }; static device_probe_t umodem_probe; static device_attach_t umodem_attach; static device_detach_t umodem_detach; static int umodem_open(struct ucom_softc *ucom); static void umodem_close(struct ucom_softc *ucom); static void umodem_start_read(struct ucom_softc *ucom); static void umodem_stop_read(struct ucom_softc *ucom); static void umodem_start_write(struct ucom_softc *ucom); static void umodem_stop_write(struct ucom_softc *ucom); static void umodem_get_caps(struct usbd_device *udev, u_int8_t *cm, u_int8_t *acm); static void umodem_get_status(struct ucom_softc *ucom, u_int8_t *lsr, u_int8_t *msr); static int umodem_param(struct ucom_softc *ucom, struct termios *t); static int umodem_ioctl(struct ucom_softc *ucom, uint32_t cmd, caddr_t data, int flag, struct thread *td); static void umodem_set_dtr(struct ucom_softc *ucom, u_int8_t onoff); static void umodem_set_rts(struct ucom_softc *ucom, u_int8_t onoff); static void umodem_set_break(struct ucom_softc *ucom, u_int8_t onoff); static void umodem_intr_callback(struct usbd_xfer *xfer); static void umodem_intr_clear_stall_callback(struct usbd_xfer *xfer); static void umodem_set_line_state_callback(struct usbd_xfer *xfer); static void umodem_set_break_callback(struct usbd_xfer *xfer); static void umodem_write_callback(struct usbd_xfer *xfer); static void umodem_read_callback(struct usbd_xfer *xfer); static void umodem_write_clear_stall_callback(struct usbd_xfer *xfer); static void umodem_read_clear_stall_callback(struct usbd_xfer *xfer); static void umodem_set_line_coding_callback(struct usbd_xfer *xfer); static void * umodem_get_desc(struct usbd_device *udev, u_int8_t type, u_int8_t subtype); static usbd_status umodem_set_comm_feature(struct usbd_device *udev, u_int8_t iface_no, int feature, int state); static const struct usbd_config umodem_config_data[UMODEM_N_DATA_TRANSFER] = { [0] = { .type = UE_BULK, .endpoint = -1, /* any */ .direction = UE_DIR_OUT, .bufsize = UMODEM_OBUFSIZE, .flags = 0, .callback = &umodem_write_callback, }, [1] = { .type = UE_BULK, .endpoint = -1, /* any */ .direction = UE_DIR_IN, .bufsize = UMODEM_IBUFSIZE, .flags = USBD_SHORT_XFER_OK, .callback = &umodem_read_callback, }, [2] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .callback = &umodem_write_clear_stall_callback, .timeout = 1000, /* 1 second */ }, [3] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .callback = &umodem_read_clear_stall_callback, .timeout = 1000, /* 1 second */ }, [4] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .callback = &umodem_set_line_state_callback, .timeout = 1000, /* 1 second */ }, [5] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .callback = &umodem_set_break_callback, .timeout = 1000, /* 1 second */ }, [6] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t) + sizeof(usb_cdc_line_state_t), .callback = &umodem_set_line_coding_callback, .timeout = 1000, /* 1 second */ }, }; static const struct usbd_config umodem_config_intr[UMODEM_N_INTR_TRANSFER] = { [0] = { .type = UE_INTERRUPT, .endpoint = -1, /* any */ .direction = UE_DIR_IN, .flags = USBD_SHORT_XFER_OK, .bufsize = 0, /* use wMaxPacketSize */ .callback = &umodem_intr_callback, }, [1] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .callback = &umodem_intr_clear_stall_callback, .timeout = 1000, /* 1 second */ }, }; static const struct ucom_callback umodem_callback = { .ucom_get_status = &umodem_get_status, .ucom_set_dtr = &umodem_set_dtr, .ucom_set_rts = &umodem_set_rts, .ucom_set_break = &umodem_set_break, .ucom_param = &umodem_param, .ucom_ioctl = &umodem_ioctl, .ucom_open = &umodem_open, .ucom_close = &umodem_close, .ucom_start_read = &umodem_start_read, .ucom_stop_read = &umodem_stop_read, .ucom_start_write = &umodem_start_write, .ucom_stop_write = &umodem_stop_write, }; static device_method_t umodem_methods[] = { DEVMETHOD(device_probe, umodem_probe), DEVMETHOD(device_attach, umodem_attach), DEVMETHOD(device_detach, umodem_detach), { 0, 0 } }; static devclass_t umodem_devclass; static driver_t umodem_driver = { .name = "umodem", .methods = umodem_methods, .size = sizeof(struct umodem_softc), }; DRIVER_MODULE(umodem, uhub, umodem_driver, umodem_devclass, usbd_driver_load, 0); MODULE_DEPEND(umodem, usb, 1, 1, 1); MODULE_DEPEND(umodem, ucom, UCOM_MINVER, UCOM_PREFVER, UCOM_MAXVER); MODULE_VERSION(umodem, UMODEM_MODVER); static int umodem_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); usb_interface_descriptor_t *id; const struct umodem_product *up = umodem_products; u_int8_t cm, acm; int error = UMATCH_NONE; DPRINTF(10, "\n"); if (uaa->iface == NULL) { goto done; } id = usbd_get_interface_descriptor(uaa->iface); if (id == NULL) { goto done; } while (up->vendor) { if ((up->vendor == uaa->vendor) && (up->product == uaa->product) && (up->interface == id->bInterfaceNumber)) { error = UMATCH_VENDOR_PRODUCT; break; } up++; } if ((error == UMATCH_NONE) && (id->bInterfaceClass == UICLASS_CDC) && (id->bInterfaceSubClass == UISUBCLASS_ABSTRACT_CONTROL_MODEL) && (id->bInterfaceProtocol == UIPROTO_CDC_AT)) { error = UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO; } if (error == UMATCH_NONE) { goto done; } umodem_get_caps(uaa->device, &cm, &acm); if (!(cm & USB_CDC_CM_DOES_CM) || !(cm & USB_CDC_CM_OVER_DATA) || !(acm & USB_CDC_ACM_HAS_LINE)) { error = UMATCH_NONE; } done: return error; } static int umodem_attach(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct umodem_softc *sc = device_get_softc(dev); usb_interface_descriptor_t *id; usb_cdc_cm_descriptor_t *cmd; struct usbd_interface *iface; u_int8_t i; int error; if (sc == NULL) { return ENOMEM; } usbd_set_desc(dev, uaa->device); id = usbd_get_interface_descriptor(uaa->iface); sc->sc_ctrl_iface_no = id->bInterfaceNumber; sc->sc_ctrl_iface_index = uaa->iface_index; umodem_get_caps(uaa->device, &sc->sc_cm_cap, &sc->sc_acm_cap); /* get the data interface number */ cmd = umodem_get_desc(uaa->device, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM); if ((cmd == NULL) || (cmd->bLength < sizeof(*cmd))) { device_printf(dev, "no CM descriptor!\n"); goto detach; } sc->sc_data_iface_no = cmd->bDataInterface; device_printf(dev, "data interface %d, has %sCM over " "data, has %sbreak\n", sc->sc_data_iface_no, sc->sc_cm_cap & USB_CDC_CM_OVER_DATA ? "" : "no ", sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK ? "" : "no "); /* get the data interface too */ for (i = 0; ; i++) { iface = usbd_get_iface(uaa->device, i); if (iface) { id = usbd_get_interface_descriptor(iface); if (id && (id->bInterfaceNumber == sc->sc_data_iface_no)) { sc->sc_data_iface_index = i; USBD_SET_IFACE_NO_PROBE(uaa->device, i); break; } } else { device_printf(dev, "no data interface!\n"); goto detach; } } if (usbd_get_quirks(uaa->device)->uq_flags & UQ_ASSUME_CM_OVER_DATA) { DPRINTF(0, "Quirk says to assume CM over data\n"); sc->sc_cm_over_data = 1; } else { if (sc->sc_cm_cap & USB_CDC_CM_OVER_DATA) { if (sc->sc_acm_cap & USB_CDC_ACM_HAS_FEATURE) { error = umodem_set_comm_feature (uaa->device, sc->sc_ctrl_iface_no, UCDC_ABSTRACT_STATE, UCDC_DATA_MULTIPLEXED); if (error) { device_printf(dev, "could not set data " "multiplex mode\n"); goto detach; } } sc->sc_cm_over_data = 1; } } error = usbd_transfer_setup(uaa->device, sc->sc_data_iface_index, sc->sc_xfer_data, umodem_config_data, UMODEM_N_DATA_TRANSFER, sc, &Giant); if (error) { goto detach; } error = usbd_transfer_setup(uaa->device, sc->sc_ctrl_iface_index, sc->sc_xfer_intr, umodem_config_intr, UMODEM_N_INTR_TRANSFER, sc, &Giant); if (error) { /* ignore */ DPRINTF(0, "no interrupt pipe!\n"); } sc->sc_rts = -1; sc->sc_dtr = -1; sc->sc_break = -1; error = ucom_attach(&(sc->sc_ucom), 1, sc, &umodem_callback, &Giant); if (error) { goto detach; } return 0; detach: umodem_detach(dev); return ENXIO; } static int umodem_open(struct ucom_softc *ucom) { struct umodem_softc *sc = ucom->sc_parent; DPRINTF(0, "sc=%p\n", sc); /* clear stall first */ sc->sc_flag |= (UMODEM_FLAG_READ_STALL| UMODEM_FLAG_WRITE_STALL); if (sc->sc_xfer_intr[0]) { usbd_transfer_start(sc->sc_xfer_intr[0]); } return 0; } static void umodem_close(struct ucom_softc *ucom) { struct umodem_softc *sc = ucom->sc_parent; DPRINTF(0, "sc=%p\n", sc); if (sc->sc_xfer_intr[0]) { usbd_transfer_stop(sc->sc_xfer_intr[0]); } return; } static void umodem_start_read(struct ucom_softc *ucom) { struct umodem_softc *sc = ucom->sc_parent; usbd_transfer_start(sc->sc_xfer_data[1]); return; } static void umodem_stop_read(struct ucom_softc *ucom) { struct umodem_softc *sc = ucom->sc_parent; usbd_transfer_stop(sc->sc_xfer_data[3]); usbd_transfer_stop(sc->sc_xfer_data[1]); return; } static void umodem_start_write(struct ucom_softc *ucom) { struct umodem_softc *sc = ucom->sc_parent; usbd_transfer_start(sc->sc_xfer_data[0]); return; } static void umodem_stop_write(struct ucom_softc *ucom) { struct umodem_softc *sc = ucom->sc_parent; usbd_transfer_stop(sc->sc_xfer_data[2]); usbd_transfer_stop(sc->sc_xfer_data[0]); return; } static void umodem_get_caps(struct usbd_device *udev, u_int8_t *cm, u_int8_t *acm) { usb_cdc_cm_descriptor_t *cmd; usb_cdc_acm_descriptor_t *cad; *cm = *acm = 0; cmd = umodem_get_desc(udev, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM); if ((cmd == NULL) || (cmd->bLength < sizeof(*cmd))) { DPRINTF(0, "no CM desc\n"); return; } *cm = cmd->bmCapabilities; cad = umodem_get_desc(udev, UDESC_CS_INTERFACE, UDESCSUB_CDC_ACM); if ((cad == NULL) || (cad->bLength < sizeof(*cad))) { DPRINTF(0, "no ACM desc\n"); return; } *acm = cad->bmCapabilities; return; } static void umodem_get_status(struct ucom_softc *ucom, u_int8_t *lsr, u_int8_t *msr) { struct umodem_softc *sc = ucom->sc_parent; DPRINTF(0, "\n"); if (lsr) { *lsr = sc->sc_lsr; } if (msr) { *msr = sc->sc_msr; } return; } static int umodem_param(struct ucom_softc *ucom, struct termios *t) { struct umodem_softc *sc = ucom->sc_parent; DPRINTF(0, "sc=%p\n", sc); USETDW(sc->sc_line_state.dwDTERate, t->c_ospeed); sc->sc_line_state.bCharFormat = (t->c_cflag & CSTOPB) ? UCDC_STOP_BIT_2 : UCDC_STOP_BIT_1; sc->sc_line_state.bParityType = (t->c_cflag & PARENB) ? ((t->c_cflag & PARODD) ? UCDC_PARITY_ODD : UCDC_PARITY_EVEN) : UCDC_PARITY_NONE; switch (t->c_cflag & CSIZE) { case CS5: sc->sc_line_state.bDataBits = 5; break; case CS6: sc->sc_line_state.bDataBits = 6; break; case CS7: sc->sc_line_state.bDataBits = 7; break; case CS8: sc->sc_line_state.bDataBits = 8; break; } sc->sc_flag |= UMODEM_FLAG_SET_LC; usbd_transfer_start(sc->sc_xfer_data[6]); return 0; } static int umodem_ioctl(struct ucom_softc *ucom, uint32_t cmd, caddr_t data, int flag, struct thread *td) { struct umodem_softc *sc = ucom->sc_parent; int error = 0; DPRINTF(0, "cmd=0x%08x\n", cmd); switch (cmd) { case USB_GET_CM_OVER_DATA: *(int *)data = sc->sc_cm_over_data; break; case USB_SET_CM_OVER_DATA: if (*(int *)data != sc->sc_cm_over_data) { /* XXX change it */ } break; default: DPRINTF(0, "unknown\n"); error = ENOTTY; break; } return (error); } static void umodem_set_dtr(struct ucom_softc *ucom, u_int8_t onoff) { struct umodem_softc *sc = ucom->sc_parent; DPRINTF(0, "onoff=%d\n", onoff); if (sc->sc_dtr != onoff) { sc->sc_dtr = onoff; sc->sc_flag |= UMODEM_FLAG_SET_LS; usbd_transfer_start(sc->sc_xfer_data[4]); } return; } static void umodem_set_rts(struct ucom_softc *ucom, u_int8_t onoff) { struct umodem_softc *sc = ucom->sc_parent; DPRINTF(0, "onoff=%d\n", onoff); if (sc->sc_rts != onoff) { sc->sc_rts = onoff; sc->sc_flag |= UMODEM_FLAG_SET_LS; usbd_transfer_start(sc->sc_xfer_data[4]); } return; } static void umodem_set_break(struct ucom_softc *ucom, u_int8_t onoff) { struct umodem_softc *sc = ucom->sc_parent; DPRINTF(0, "onoff=%d\n", onoff); if (sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK) { sc->sc_break = onoff; sc->sc_flag |= UMODEM_FLAG_SEND_BREAK; usbd_transfer_start(sc->sc_xfer_data[5]); } return; } static void umodem_intr_callback(struct usbd_xfer *xfer) { usb_cdc_notification_t *nbuf = xfer->buffer; struct umodem_softc *sc = xfer->priv_sc; u_int16_t wLength; USBD_CHECK_STATUS(xfer); tr_error: if (xfer->error != USBD_CANCELLED) { sc->sc_flag |= UMODEM_FLAG_INTR_STALL; usbd_transfer_start(sc->sc_xfer_intr[1]); } return; tr_transferred: if (xfer->actlen >= 8) { if (nbuf->bmRequestType != UCDC_NOTIFICATION) { DPRINTF(0, "unknown message type, " "0x%02x, on notify pipe!\n", nbuf->bmRequestType); goto tr_setup; } switch (nbuf->bNotification) { case UCDC_N_SERIAL_STATE: /* * Set the serial state in ucom driver based on * the bits from the notify message */ wLength = UGETW(nbuf->wLength); if ((wLength < 2) || ((wLength + 8) < xfer->actlen)) { DPRINTF(0, "Invalid notification length, " "%d bytes!\n", wLength); break; } DPRINTF(0, "notify bytes = %02x%02x\n", nbuf->data[0], nbuf->data[1]); /* Currently, lsr is always zero. */ sc->sc_lsr = 0; sc->sc_msr = 0; if (nbuf->data[0] & UCDC_N_SERIAL_RI) { sc->sc_msr |= SER_RI; } if (nbuf->data[0] & UCDC_N_SERIAL_DSR) { sc->sc_msr |= SER_DSR; } if (nbuf->data[0] & UCDC_N_SERIAL_DCD) { sc->sc_msr |= SER_DCD; } ucom_status_change(&(sc->sc_ucom)); break; default: DPRINTF(0, "unknown notify message: %02x\n", nbuf->bNotification); break; } } else { DPRINTF(0, "received short packet, " "%d bytes\n", xfer->actlen); } tr_setup: if (sc->sc_flag & UMODEM_FLAG_INTR_STALL) { usbd_transfer_start(sc->sc_xfer_intr[1]); } else { usbd_start_hardware(xfer); } return; } static void umodem_intr_clear_stall_callback(struct usbd_xfer *xfer) { struct umodem_softc *sc = xfer->priv_sc; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, sc->sc_xfer_intr[0]); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, sc->sc_xfer_intr[0]); sc->sc_flag &= ~UMODEM_FLAG_INTR_STALL; usbd_transfer_start(sc->sc_xfer_intr[0]); return; tr_error: sc->sc_flag &= ~UMODEM_FLAG_INTR_STALL; DPRINTF(0, "clear stall failed, error=%s\n", usbd_errstr(xfer->error)); return; } static void umodem_set_line_state_callback(struct usbd_xfer *xfer) { usb_device_request_t *req = xfer->buffer; struct umodem_softc *sc = xfer->priv_sc; u_int16_t temp; USBD_CHECK_STATUS(xfer); tr_error: DPRINTF(0, "error=%s\n", usbd_errstr(xfer->error)); tr_setup: tr_transferred: if (sc->sc_flag & UMODEM_FLAG_SET_LS) { sc->sc_flag &= ~UMODEM_FLAG_SET_LS; temp = ((sc->sc_dtr ? UCDC_LINE_DTR : 0) | (sc->sc_rts ? UCDC_LINE_RTS : 0)); req->bmRequestType = UT_WRITE_CLASS_INTERFACE; req->bRequest = UCDC_SET_CONTROL_LINE_STATE; USETW(req->wValue, temp); USETW(req->wIndex, sc->sc_ctrl_iface_no); USETW(req->wLength, 0); usbd_start_hardware(xfer); } return; } static void umodem_set_break_callback(struct usbd_xfer *xfer) { usb_device_request_t *req = xfer->buffer; struct umodem_softc *sc = xfer->priv_sc; u_int16_t temp; USBD_CHECK_STATUS(xfer); tr_error: DPRINTF(0, "error=%s\n", usbd_errstr(xfer->error)); tr_setup: tr_transferred: if (sc->sc_flag & UMODEM_FLAG_SEND_BREAK) { sc->sc_flag &= ~UMODEM_FLAG_SEND_BREAK; temp = sc->sc_break ? UCDC_BREAK_ON : UCDC_BREAK_OFF; req->bmRequestType = UT_WRITE_CLASS_INTERFACE; req->bRequest = UCDC_SEND_BREAK; USETW(req->wValue, temp); USETW(req->wIndex, sc->sc_ctrl_iface_no); USETW(req->wLength, 0); usbd_start_hardware(xfer); } return; } static void umodem_set_line_coding_callback(struct usbd_xfer *xfer) { usb_device_request_t *req = xfer->buffer; usb_cdc_line_state_t *ls = (void *)(req+1); struct umodem_softc *sc = xfer->priv_sc; USBD_CHECK_STATUS(xfer); tr_error: DPRINTF(0, "error=%s\n", usbd_errstr(xfer->error)); tr_setup: tr_transferred: if (sc->sc_flag & UMODEM_FLAG_SET_LC) { sc->sc_flag &= ~UMODEM_FLAG_SET_LC; DPRINTF(0, "rate=%d fmt=%d parity=%d bits=%d\n", UGETDW(ls->dwDTERate), ls->bCharFormat, ls->bParityType, ls->bDataBits); req->bmRequestType = UT_WRITE_CLASS_INTERFACE; req->bRequest = UCDC_SET_LINE_CODING; USETW(req->wValue, 0); USETW(req->wIndex, sc->sc_ctrl_iface_no); USETW(req->wLength, sizeof(*ls)); *ls = sc->sc_line_state; usbd_start_hardware(xfer); } return; } static void umodem_write_callback(struct usbd_xfer *xfer) { struct umodem_softc *sc = xfer->priv_sc; u_int32_t actlen; USBD_CHECK_STATUS(xfer); tr_error: if (xfer->error != USBD_CANCELLED) { sc->sc_flag |= UMODEM_FLAG_WRITE_STALL; usbd_transfer_start(sc->sc_xfer_data[2]); } return; tr_setup: tr_transferred: if (sc->sc_flag & UMODEM_FLAG_WRITE_STALL) { usbd_transfer_start(sc->sc_xfer_data[2]); return; } if(ucom_get_data(&(sc->sc_ucom), xfer->buffer, UMODEM_OBUFSIZE, &actlen)) { xfer->length = actlen; usbd_start_hardware(xfer); } return; } static void umodem_write_clear_stall_callback(struct usbd_xfer *xfer) { struct umodem_softc *sc = xfer->priv_sc; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, sc->sc_xfer_data[0]); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, sc->sc_xfer_data[0]); sc->sc_flag &= ~UMODEM_FLAG_WRITE_STALL; usbd_transfer_start(sc->sc_xfer_data[0]); return; tr_error: sc->sc_flag &= ~UMODEM_FLAG_WRITE_STALL; DPRINTF(0, "clear stall failed, error=%s\n", usbd_errstr(xfer->error)); return; } static void umodem_read_callback(struct usbd_xfer *xfer) { struct umodem_softc *sc = xfer->priv_sc; USBD_CHECK_STATUS(xfer); tr_error: if (xfer->error != USBD_CANCELLED) { sc->sc_flag |= UMODEM_FLAG_READ_STALL; usbd_transfer_start(sc->sc_xfer_data[3]); } return; tr_transferred: ucom_put_data(&(sc->sc_ucom), xfer->buffer, xfer->actlen); tr_setup: if (sc->sc_flag & UMODEM_FLAG_READ_STALL) { usbd_transfer_start(sc->sc_xfer_data[3]); } else { usbd_start_hardware(xfer); } return; } static void umodem_read_clear_stall_callback(struct usbd_xfer *xfer) { struct umodem_softc *sc = xfer->priv_sc; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, sc->sc_xfer_data[1]); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, sc->sc_xfer_data[1]); sc->sc_flag &= ~UMODEM_FLAG_READ_STALL; usbd_transfer_start(sc->sc_xfer_data[1]); return; tr_error: sc->sc_flag &= ~UMODEM_FLAG_READ_STALL; DPRINTF(0, "clear stall failed, error=%s\n", usbd_errstr(xfer->error)); return; } static void * umodem_get_desc(struct usbd_device *udev, u_int8_t type, u_int8_t subtype) { usb_config_descriptor_t *cd = usbd_get_config_descriptor(udev); usb_descriptor_t *desc = usbd_find_descriptor(cd, type, subtype); return desc; } static usbd_status umodem_set_comm_feature(struct usbd_device *udev, u_int8_t iface_no, int feature, int state) { usb_device_request_t req; usb_cdc_abstract_state_t ast; DPRINTF(0, "feature=%d state=%d\n", feature, state); req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UCDC_SET_COMM_FEATURE; USETW(req.wValue, feature); USETW(req.wIndex, iface_no); USETW(req.wLength, UCDC_ABSTRACT_STATE_LENGTH); USETW(ast.wState, state); return usbd_do_request(udev, &req, &ast); } static int umodem_detach(device_t dev) { struct umodem_softc *sc = device_get_softc(dev); DPRINTF(0, "sc=%p\n", sc); ucom_detach(&(sc->sc_ucom), 1); usbd_transfer_unsetup(sc->sc_xfer_intr, UMODEM_N_INTR_TRANSFER); usbd_transfer_unsetup(sc->sc_xfer_data, UMODEM_N_DATA_TRANSFER); return 0; } pwcbsd/usb/ums.c000644 000423 000000 00000051264 10551670754 014361 0ustar00luigiwheel000000 000000 /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD: src/sys/dev/usb/ums.c,v 1.82 2006/09/07 00:06:42 imp Exp $"); /* * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef USB_DEBUG #define DPRINTF(n,fmt,...) \ do { if (ums_debug > (n)) { \ printf("%s: " fmt, __FUNCTION__,## __VA_ARGS__); } } while (0) static int ums_debug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, ums, CTLFLAG_RW, 0, "USB ums"); SYSCTL_INT(_hw_usb_ums, OID_AUTO, debug, CTLFLAG_RW, &ums_debug, 0, "ums debug level"); #else #define DPRINTF(...) #endif #define DEV2SC(dev) (dev)->si_drv1 #define MOUSE_FLAGS_MASK (HIO_CONST|HIO_RELATIVE) #define MOUSE_FLAGS (HIO_RELATIVE) #define UMS_BUF_SIZE 8 /* bytes */ #define UMS_IFQ_MAXLEN 50 /* units */ #define UMS_N_TRANSFER 2 /* units */ #define UMS_BUTTON_MAX 31 /* exclusive, must be less than 32 */ #define UMS_BUT(i) ((i) < 3 ? (((i) + 2) % 3) : (i)) struct ums_softc { struct usb_cdev sc_cdev; struct mtx sc_mtx; struct __callout sc_callout; struct hid_location sc_loc_x; struct hid_location sc_loc_y; struct hid_location sc_loc_z; struct hid_location sc_loc_t; struct hid_location sc_loc_btn[UMS_BUTTON_MAX]; mousehw_t sc_hw; mousemode_t sc_mode; mousestatus_t sc_status; struct usbd_xfer * sc_xfer[UMS_N_TRANSFER]; u_int32_t sc_flags; #define UMS_FLAG_X_AXIS 0x0001 #define UMS_FLAG_Y_AXIS 0x0002 #define UMS_FLAG_Z_AXIS 0x0004 #define UMS_FLAG_T_AXIS 0x0008 #define UMS_FLAG_SBU 0x0010 /* spurious button up events */ #define UMS_FLAG_INTR_STALL 0x0020 /* set if transfer error */ u_int8_t sc_buttons; u_int8_t sc_iid; }; static void ums_put_queue_timeout(void *__sc); static void ums_clear_stall_callback(struct usbd_xfer *xfer); static void ums_intr_callback(struct usbd_xfer *xfer); static device_probe_t ums_probe; static device_attach_t ums_attach; static device_detach_t ums_detach; static void ums_start_read(struct usb_cdev *cdev); static void ums_stop_read(struct usb_cdev *cdev); static int32_t ums_open(struct usb_cdev *cdev, int32_t fflags, int32_t devtype, struct thread *td); static int32_t ums_ioctl(struct usb_cdev *cdev, u_long cmd, caddr_t addr, int32_t fflags, struct thread *td); static void ums_put_queue(struct ums_softc *sc, int32_t dx, int32_t dy, int32_t dz, int32_t dt, int32_t buttons); static void ums_put_queue_timeout(void *__sc) { struct ums_softc *sc = __sc; mtx_assert(&(sc->sc_mtx), MA_OWNED); ums_put_queue(sc, 0, 0, 0, 0, 0); mtx_unlock(&(sc->sc_mtx)); return; } static void ums_clear_stall_callback(struct usbd_xfer *xfer) { struct ums_softc *sc = xfer->priv_sc; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, sc->sc_xfer[0]); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, sc->sc_xfer[0]); sc->sc_flags &= ~UMS_FLAG_INTR_STALL; usbd_transfer_start(sc->sc_xfer[0]); return; tr_error: /* bomb out */ sc->sc_flags &= ~UMS_FLAG_INTR_STALL; DPRINTF(0, "clear stall failed, error=%s!\n", usbd_errstr(xfer->error)); return; } static void ums_intr_callback(struct usbd_xfer *xfer) { struct ums_softc *sc = xfer->priv_sc; struct usbd_mbuf *m; u_int8_t *buf = xfer->buffer; u_int16_t len = xfer->actlen; int32_t buttons = 0; int32_t dx; int32_t dy; int32_t dz; int32_t dt; u_int8_t i; USBD_CHECK_STATUS(xfer); tr_transferred: DPRINTF(5, "sc=%p actlen=%d\n", sc, len); DPRINTF(5, "data = %02x %02x %02x %02x " "%02x %02x %02x %02x\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]); /* * The M$ Wireless Intellimouse 2.0 sends 1 extra leading byte of * data compared to most USB mice. This byte frequently switches * from 0x01 (usual state) to 0x02. I assume it is to allow * extra, non-standard, reporting (say battery-life). However * at the same time it generates a left-click message on the button * byte which causes spurious left-click's where there shouldn't be. * This should sort that. * Currently it's the only user of UMS_FLAG_T_AXIS so use it as an identifier. * We probably should switch to some more official quirk. */ if (sc->sc_iid) { if (sc->sc_flags & UMS_FLAG_T_AXIS) { if (*buf == 0x02) { goto tr_setup; } } else { if (*buf != sc->sc_iid) { goto tr_setup; } } if (len) { len--; buf++; } else { goto tr_setup; } } dx = (sc->sc_flags & UMS_FLAG_X_AXIS) ? hid_get_data(buf, len, &sc->sc_loc_x) : 0; dy = (sc->sc_flags & UMS_FLAG_Y_AXIS) ? -hid_get_data(buf, len, &sc->sc_loc_y) : 0; dz = (sc->sc_flags & UMS_FLAG_Z_AXIS) ? -hid_get_data(buf, len, &sc->sc_loc_z) : 0; dt = (sc->sc_flags & UMS_FLAG_T_AXIS) ? -hid_get_data(buf, len, &sc->sc_loc_t) : 0; for (i = 0; i < sc->sc_buttons; i++) { if (hid_get_data(buf, len, &sc->sc_loc_btn[i])) { buttons |= (1 << UMS_BUT(i)); } } if (dx || dy || dz || dt || (buttons != sc->sc_status.button)) { DPRINTF(5, "x:%d y:%d z:%d t:%d buttons:0x%08x\n", dx, dy, dz, dt, buttons); sc->sc_status.button = buttons; sc->sc_status.dx += dx; sc->sc_status.dy += dy; sc->sc_status.dz += dz; /* sc->sc_status.dt += dt;*/ /* no way to export this yet */ /* * The Qtronix keyboard has a built in PS/2 port for a mouse. * The firmware once in a while posts a spurious button up * event. This event we ignore by doing a timeout for 50 msecs. * If we receive dx=dy=dz=buttons=0 before we add the event to * the queue. * In any other case we delete the timeout event. */ if ((sc->sc_flags & UMS_FLAG_SBU) && (dx == 0) && (dy == 0) && (dz == 0) && (dt == 0) && (buttons == 0)) { __callout_reset(&(sc->sc_callout), hz / 20, &ums_put_queue_timeout, sc); } else { __callout_stop(&(sc->sc_callout)); ums_put_queue(sc, dx, dy, dz, dt, buttons); } } tr_setup: if (sc->sc_flags & UMS_FLAG_INTR_STALL) { usbd_transfer_start(sc->sc_xfer[1]); } else { USBD_IF_POLL(&(sc->sc_cdev.sc_rdq_free), m); if (m) { usbd_start_hardware(xfer); } } return; tr_error: if (xfer->error != USBD_CANCELLED) { /* start clear stall */ sc->sc_flags |= UMS_FLAG_INTR_STALL; usbd_transfer_start(sc->sc_xfer[1]); } return; } static const struct usbd_config ums_config[UMS_N_TRANSFER] = { [0] = { .type = UE_INTERRUPT, .endpoint = -1, /* any */ .direction = UE_DIR_IN, .flags = USBD_SHORT_XFER_OK, .bufsize = 0, /* use wMaxPacketSize */ .callback = &ums_intr_callback, }, [1] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .callback = &ums_clear_stall_callback, .timeout = 1000, /* 1 second */ }, }; static int ums_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); usb_interface_descriptor_t *id; void *d_ptr; int32_t d_len; int32_t error = 0; DPRINTF(10, "\n"); if (uaa->iface == NULL) { return UMATCH_NONE; } id = usbd_get_interface_descriptor(uaa->iface); if ((id == NULL) || (id->bInterfaceClass != UICLASS_HID)) { return UMATCH_NONE; } error = usbreq_read_report_desc(uaa->device, uaa->iface_index, &d_ptr, &d_len, M_TEMP); if (error) { return UMATCH_NONE; } if (hid_is_collection(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE))) error = UMATCH_IFACECLASS; else error = UMATCH_NONE; free(d_ptr, M_TEMP); return error; } static int ums_attach(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct ums_softc *sc = device_get_softc(dev); void *d_ptr = NULL; const char * p_buf[2]; int32_t unit = device_get_unit(dev); int32_t d_len; int32_t isize; u_int32_t flags; int32_t err; u_int8_t i; char buf_1[16]; DPRINTF(10, "sc=%p\n", sc); usbd_set_desc(dev, uaa->device); mtx_init(&(sc->sc_mtx), "ums lock", NULL, MTX_DEF|MTX_RECURSE); __callout_init_mtx(&(sc->sc_callout), &(sc->sc_mtx), CALLOUT_RETURNUNLOCKED); err = usbd_transfer_setup(uaa->device, uaa->iface_index, sc->sc_xfer, ums_config, UMS_N_TRANSFER, sc, &(sc->sc_mtx)); if (err) { DPRINTF(0, "error=%s\n", usbd_errstr(err)) ; goto detach; } err = usbreq_read_report_desc(uaa->device, uaa->iface_index, &d_ptr, &d_len, M_TEMP); if (err) { device_printf(dev, "error reading report description\n"); goto detach; } if (hid_locate(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X), hid_input, &sc->sc_loc_x, &flags)) { if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) { sc->sc_flags |= UMS_FLAG_X_AXIS; } } if (hid_locate(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y), hid_input, &sc->sc_loc_y, &flags)) { if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) { sc->sc_flags |= UMS_FLAG_Y_AXIS; } } /* try to guess the Z activator: first check Z, then WHEEL */ if (hid_locate(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Z), hid_input, &sc->sc_loc_z, &flags) || hid_locate(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_WHEEL), hid_input, &sc->sc_loc_z, &flags) || hid_locate(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_TWHEEL), hid_input, &sc->sc_loc_z, &flags)) { if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) { sc->sc_flags |= UMS_FLAG_Z_AXIS; } } /* The Microsoft Wireless Intellimouse 2.0 reports it's wheel * using 0x0048, which is HUG_TWHEEL, and seems to expect you * to know that the byte after the wheel is the tilt axis. * There are no other HID axis descriptors other than X,Y and * TWHEEL */ if (hid_locate(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_TWHEEL), hid_input, &sc->sc_loc_t, &flags)) { sc->sc_loc_t.pos += 8; if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) { sc->sc_flags |= UMS_FLAG_T_AXIS; } } /* figure out the number of buttons */ for (i = 0; i < UMS_BUTTON_MAX; i++) { if (!hid_locate(d_ptr, d_len, HID_USAGE2(HUP_BUTTON, (i+1)), hid_input, &(sc->sc_loc_btn[i]), NULL)) { break; } } sc->sc_buttons = i; device_printf(dev, "%d buttons and [%s%s%s%s] coordinates\n", (sc->sc_buttons), (sc->sc_flags & UMS_FLAG_X_AXIS) ? "X" : "", (sc->sc_flags & UMS_FLAG_Y_AXIS) ? "Y" : "", (sc->sc_flags & UMS_FLAG_Z_AXIS) ? "Z" : "", (sc->sc_flags & UMS_FLAG_T_AXIS) ? "T" : ""); isize = hid_report_size(d_ptr, d_len, hid_input, &sc->sc_iid); if (isize > sc->sc_xfer[0]->length) { DPRINTF(0, "WARNING: report size, %d bytes, is larger " "than interrupt size, %d bytes!\n", isize, sc->sc_xfer[0]->length); } free(d_ptr, M_TEMP); d_ptr = NULL; #ifdef USB_DEBUG DPRINTF(0, "sc=%p\n", sc); DPRINTF(0, "X\t%d/%d\n", sc->sc_loc_x.pos, sc->sc_loc_x.size); DPRINTF(0, "Y\t%d/%d\n", sc->sc_loc_y.pos, sc->sc_loc_y.size); DPRINTF(0, "Z\t%d/%d\n", sc->sc_loc_z.pos, sc->sc_loc_z.size); DPRINTF(0, "T\t%d/%d\n", sc->sc_loc_t.pos, sc->sc_loc_t.size); for (i = 0; i < sc->sc_buttons; i++) { DPRINTF(0, "B%d\t%d/%d\n", i+1 , sc->sc_loc_btn[i].pos, sc->sc_loc_btn[i].size); } DPRINTF(0, "size=%d, id=%d\n", isize, sc->sc_iid); #endif if (sc->sc_buttons > MOUSE_MSC_MAXBUTTON) sc->sc_hw.buttons = MOUSE_MSC_MAXBUTTON; else sc->sc_hw.buttons = sc->sc_buttons; sc->sc_hw.iftype = MOUSE_IF_USB; sc->sc_hw.type = MOUSE_MOUSE; sc->sc_hw.model = MOUSE_MODEL_GENERIC; sc->sc_hw.hwid = 0; sc->sc_mode.protocol = MOUSE_PROTO_MSC; sc->sc_mode.rate = -1; sc->sc_mode.resolution = MOUSE_RES_UNKNOWN; sc->sc_mode.accelfactor = 0; sc->sc_mode.level = 0; sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE; sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK; sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC; sc->sc_status.flags = 0; sc->sc_status.button = 0; sc->sc_status.obutton = 0; sc->sc_status.dx = 0; sc->sc_status.dy = 0; sc->sc_status.dz = 0; snprintf(buf_1, sizeof(buf_1), "ums%d", unit); p_buf[0] = buf_1; p_buf[1] = NULL; sc->sc_cdev.sc_start_read = &ums_start_read; sc->sc_cdev.sc_stop_read = &ums_stop_read; sc->sc_cdev.sc_open = &ums_open; sc->sc_cdev.sc_ioctl = &ums_ioctl; sc->sc_cdev.sc_flags |= (USB_CDEV_FLAG_FWD_SHORT| USB_CDEV_FLAG_WAKEUP_RD_IMMED| USB_CDEV_FLAG_WAKEUP_WR_IMMED); err = usb_cdev_attach(&(sc->sc_cdev), sc, &(sc->sc_mtx), p_buf, UID_ROOT, GID_OPERATOR, 0644, UMS_BUF_SIZE, UMS_IFQ_MAXLEN, 1, 1 /* dummy write buffer */); if (err) { goto detach; } return 0; detach: if (d_ptr) { free(d_ptr, M_TEMP); } ums_detach(dev); return ENOMEM; } static int ums_detach(device_t self) { struct ums_softc *sc = device_get_softc(self); DPRINTF(0, "sc=%p\n", sc); usb_cdev_detach(&(sc->sc_cdev)); usbd_transfer_unsetup(sc->sc_xfer, UMS_N_TRANSFER); __callout_drain(&(sc->sc_callout)); mtx_destroy(&(sc->sc_mtx)); return 0; } static void ums_start_read(struct usb_cdev *cdev) { struct ums_softc *sc = cdev->sc_priv_ptr; usbd_transfer_start(sc->sc_xfer[0]); return; } static void ums_stop_read(struct usb_cdev *cdev) { struct ums_softc *sc = cdev->sc_priv_ptr; usbd_transfer_stop(sc->sc_xfer[1]); usbd_transfer_stop(sc->sc_xfer[0]); __callout_stop(&(sc->sc_callout)); return; } #if ((MOUSE_SYS_PACKETSIZE != 8) || \ (MOUSE_MSC_PACKETSIZE != 5)) #error "Software assumptions are not met. Please update code." #endif static void ums_put_queue(struct ums_softc *sc, int32_t dx, int32_t dy, int32_t dz, int32_t dt, int32_t buttons) { u_int8_t buf[8]; if (1) { if (dx > 254) dx = 254; if (dx < -256) dx = -256; if (dy > 254) dy = 254; if (dy < -256) dy = -256; if (dz > 126) dz = 126; if (dz < -128) dz = -128; if (dt > 126) dt = 126; if (dt < -128) dt = -128; buf[0] = sc->sc_mode.syncmask[1]; buf[0] |= (~buttons) & MOUSE_MSC_BUTTONS; buf[1] = dx >> 1; buf[2] = dy >> 1; buf[3] = dx - (dx >> 1); buf[4] = dy - (dy >> 1); if (sc->sc_mode.level == 1) { buf[5] = dz >> 1; buf[6] = dz - (dz >> 1); buf[7] = (((~buttons) >> 3) & MOUSE_SYS_EXTBUTTONS); } usb_cdev_put_data(&(sc->sc_cdev), buf, sc->sc_mode.packetsize, 1); } else { DPRINTF(0, "Buffer full, discarded packet\n"); } return; } static void ums_reset_buf(struct ums_softc *sc) { struct usbd_mbuf *m; /* reset read queue */ while(1) { USBD_IF_DEQUEUE(&(sc->sc_cdev.sc_rdq_used), m); if (m) { USBD_IF_ENQUEUE(&(sc->sc_cdev.sc_rdq_free), m); } else { break; } } return; } static int32_t ums_open(struct usb_cdev *cdev, int32_t fflags, int32_t devtype, struct thread *td) { struct ums_softc *sc = cdev->sc_priv_ptr; DPRINTF(1, "\n"); /* reset status */ sc->sc_status.flags = 0; sc->sc_status.button = 0; sc->sc_status.obutton = 0; sc->sc_status.dx = 0; sc->sc_status.dy = 0; sc->sc_status.dz = 0; /* sc->sc_status.dt = 0; */ return 0; } static int32_t ums_ioctl(struct usb_cdev *cdev, u_long cmd, caddr_t addr, int32_t fflags, struct thread *td) { struct ums_softc *sc = cdev->sc_priv_ptr; mousemode_t mode; int error = 0; DPRINTF(1, "\n"); switch(cmd) { case MOUSE_GETHWINFO: *(mousehw_t *)addr = sc->sc_hw; break; case MOUSE_GETMODE: *(mousemode_t *)addr = sc->sc_mode; break; case MOUSE_SETMODE: mode = *(mousemode_t *)addr; if (mode.level == -1) { /* don't change the current setting */ } else if ((mode.level < 0) || (mode.level > 1)) { error = EINVAL; goto done; } else { sc->sc_mode.level = mode.level; } if (sc->sc_mode.level == 0) { if (sc->sc_buttons > MOUSE_MSC_MAXBUTTON) sc->sc_hw.buttons = MOUSE_MSC_MAXBUTTON; else sc->sc_hw.buttons = sc->sc_buttons; sc->sc_mode.protocol = MOUSE_PROTO_MSC; sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE; sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK; sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC; } else if (sc->sc_mode.level == 1) { if (sc->sc_buttons > MOUSE_SYS_MAXBUTTON) sc->sc_hw.buttons = MOUSE_SYS_MAXBUTTON; else sc->sc_hw.buttons = sc->sc_buttons; sc->sc_mode.protocol = MOUSE_PROTO_SYSMOUSE; sc->sc_mode.packetsize = MOUSE_SYS_PACKETSIZE; sc->sc_mode.syncmask[0] = MOUSE_SYS_SYNCMASK; sc->sc_mode.syncmask[1] = MOUSE_SYS_SYNC; } ums_reset_buf(sc); break; case MOUSE_GETLEVEL: *(int *)addr = sc->sc_mode.level; break; case MOUSE_SETLEVEL: if (*(int *)addr < 0 || *(int *)addr > 1) { error = EINVAL; goto done; } sc->sc_mode.level = *(int *)addr; if (sc->sc_mode.level == 0) { if (sc->sc_buttons > MOUSE_MSC_MAXBUTTON) sc->sc_hw.buttons = MOUSE_MSC_MAXBUTTON; else sc->sc_hw.buttons = sc->sc_buttons; sc->sc_mode.protocol = MOUSE_PROTO_MSC; sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE; sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK; sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC; } else if (sc->sc_mode.level == 1) { if (sc->sc_buttons > MOUSE_SYS_MAXBUTTON) sc->sc_hw.buttons = MOUSE_SYS_MAXBUTTON; else sc->sc_hw.buttons = sc->sc_buttons; sc->sc_mode.protocol = MOUSE_PROTO_SYSMOUSE; sc->sc_mode.packetsize = MOUSE_SYS_PACKETSIZE; sc->sc_mode.syncmask[0] = MOUSE_SYS_SYNCMASK; sc->sc_mode.syncmask[1] = MOUSE_SYS_SYNC; } ums_reset_buf(sc); break; case MOUSE_GETSTATUS: { mousestatus_t *status = (mousestatus_t *) addr; *status = sc->sc_status; sc->sc_status.obutton = sc->sc_status.button; sc->sc_status.button = 0; sc->sc_status.dx = 0; sc->sc_status.dy = 0; sc->sc_status.dz = 0; /* sc->sc_status.dt = 0; */ if (status->dx || status->dy || status->dz /* || status->dt */) { status->flags |= MOUSE_POSCHANGED; } if (status->button != status->obutton) { status->flags |= MOUSE_BUTTONSCHANGED; } break; } default: error = ENOTTY; } done: return error; } static devclass_t ums_devclass; static device_method_t ums_methods[] = { DEVMETHOD(device_probe, ums_probe), DEVMETHOD(device_attach, ums_attach), DEVMETHOD(device_detach, ums_detach), { 0, 0 } }; static driver_t ums_driver = { .name = "ums", .methods = ums_methods, .size = sizeof(struct ums_softc), }; DRIVER_MODULE(ums, uhub, ums_driver, ums_devclass, usbd_driver_load, 0); MODULE_DEPEND(ums, usb, 1, 1, 1); pwcbsd/usb/uplcom.c000644 000423 000000 00000073536 10551670754 015062 0ustar00luigiwheel000000 000000 /* $NetBSD: uplcom.c,v 1.21 2001/11/13 06:24:56 lukem Exp $ */ /*- * Copyright (c) 2001-2003, 2005 Shunsuke Akiyama . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /*- * Copyright (c) 2001 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Ichiro FUKUHARA (ichiro@ichiro.org). * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * This driver supports several USB-to-RS232 serial adapters driven by * Prolific PL-2303, PL-2303X and probably PL-2303HX USB-to-RS232 * bridge chip. The adapters are sold under many different brand * names. * * Datasheets are available at Prolific www site at * http://www.prolific.com.tw. The datasheets don't contain full * programming information for the chip. * * PL-2303HX is probably programmed the same as PL-2303X. * * There are several differences between PL-2303 and PL-2303(H)X. * PL-2303(H)X can do higher bitrate in bulk mode, has _probably_ * different command for controlling CRTSCTS and needs special * sequence of commands for initialization which aren't also * documented in the datasheet. */ #include "opt_uplcom.h" /* XXX remove this */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" __FBSDID("$FreeBSD: src/sys/dev/usb/uplcom.c $"); #ifdef USB_DEBUG #define DPRINTF(n,fmt,...) \ do { if (uplcom_debug > (n)) { \ printf("%s: " fmt, __FUNCTION__,## __VA_ARGS__); } } while (0) static int uplcom_debug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, uplcom, CTLFLAG_RW, 0, "USB uplcom"); SYSCTL_INT(_hw_usb_uplcom, OID_AUTO, debug, CTLFLAG_RW, &uplcom_debug, 0, "uplcom debug level"); #else #define DPRINTF(...) #endif #define UPLCOM_MODVER 1 /* module version */ #define UPLCOM_CONFIG_INDEX 0 #define UPLCOM_IFACE_INDEX 0 #define UPLCOM_SECOND_IFACE_INDEX 1 #ifndef UPLCOM_INTR_INTERVAL #define UPLCOM_INTR_INTERVAL 0 /* default */ #endif /* * These are the maximum number of bytes transferred per frame. * The output buffer size cannot be increased due to the size encoding. */ #define UPLCOM_IBUFSIZE 256 #define UPLCOM_OBUFSIZE 256 #define UPLCOM_N_DATA_TRANSFER 7 #define UPLCOM_N_INTR_TRANSFER 2 #define UPLCOM_SET_REQUEST 0x01 #define UPLCOM_SET_CRTSCTS 0x41 #define UPLCOM_SET_CRTSCTS_PL2303X 0x61 #define RSAQ_STATUS_CTS 0x80 #define RSAQ_STATUS_DSR 0x02 #define RSAQ_STATUS_DCD 0x01 #define TYPE_PL2303 0 #define TYPE_PL2303X 1 struct uplcom_softc { struct ucom_softc sc_ucom; usb_cdc_line_state_t sc_line_state; /* current line state */ struct usbd_xfer * sc_xfer_intr[UPLCOM_N_INTR_TRANSFER]; struct usbd_xfer * sc_xfer_data[UPLCOM_N_DATA_TRANSFER]; u_int16_t sc_flag; #define UPLCOM_FLAG_SET_LS 0x0001 #define UPLCOM_FLAG_SET_BREAK 0x0002 #define UPLCOM_FLAG_SET_LC 0x0004 #define UPLCOM_FLAG_SET_CRTSCTS 0x0008 #define UPLCOM_FLAG_INTR_STALL 0x0010 #define UPLCOM_FLAG_READ_STALL 0x0020 #define UPLCOM_FLAG_WRITE_STALL 0x0040 u_int8_t sc_dtr; /* current DTR state */ u_int8_t sc_rts; /* current RTS state */ u_int8_t sc_break; /* current BREAK state */ u_int8_t sc_lsr; /* local status register */ u_int8_t sc_msr; /* uplcom status register */ u_int8_t sc_chiptype; /* type of chip */ u_int8_t sc_ctrl_iface_no; u_int8_t sc_ctrl_iface_index; u_int8_t sc_data_iface_no; u_int8_t sc_data_iface_index; u_int8_t sc_crtscts; }; static const struct uplcom_product * uplcom_find_up(struct usb_attach_arg *uaa); static usbd_status uplcom_reset(struct uplcom_softc *sc, struct usbd_device *udev); static int uplcom_pl2303x_init(struct usbd_device *udev); static void uplcom_set_dtr(struct ucom_softc *ucom, u_int8_t onoff); static void uplcom_set_rts(struct ucom_softc *ucom, u_int8_t onoff); static void uplcom_set_break(struct ucom_softc *sc, u_int8_t onoff); static int uplcom_param(struct ucom_softc *ucom, struct termios *t); static int uplcom_open(struct ucom_softc *ucom); static void uplcom_close(struct ucom_softc *ucom); static void uplcom_start_read(struct ucom_softc *ucom); static void uplcom_stop_read(struct ucom_softc *ucom); static void uplcom_start_write(struct ucom_softc *ucom); static void uplcom_stop_write(struct ucom_softc *ucom); static void uplcom_get_status(struct ucom_softc *ucom, u_int8_t *lsr, u_int8_t *msr); static int uplcom_ioctl(struct ucom_softc *ucom, uint32_t cmd, caddr_t data, int flag, struct thread *td); static void uplcom_set_line_state_callback(struct usbd_xfer *xfer); static void uplcom_set_break_callback(struct usbd_xfer *xfer); static void uplcom_set_line_coding_callback(struct usbd_xfer *xfer); static void uplcom_intr_callback(struct usbd_xfer *xfer); static void uplcom_intr_clear_stall_callback(struct usbd_xfer *xfer); static void uplcom_write_callback(struct usbd_xfer *xfer); static void uplcom_write_clear_stall_callback(struct usbd_xfer *xfer); static void uplcom_read_callback(struct usbd_xfer *xfer); static void uplcom_read_clear_stall_callback(struct usbd_xfer *xfer); static device_probe_t uplcom_probe; static device_attach_t uplcom_attach; static device_detach_t uplcom_detach; static const struct usbd_config uplcom_config_data[UPLCOM_N_DATA_TRANSFER] = { [0] = { .type = UE_BULK, .endpoint = -1, /* any */ .direction = UE_DIR_OUT, .bufsize = UPLCOM_OBUFSIZE, .flags = 0, .callback = &uplcom_write_callback, }, [1] = { .type = UE_BULK, .endpoint = -1, /* any */ .direction = UE_DIR_IN, .bufsize = UPLCOM_IBUFSIZE, .flags = USBD_SHORT_XFER_OK, .callback = &uplcom_read_callback, }, [2] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .callback = &uplcom_write_clear_stall_callback, .timeout = 1000, /* 1 second */ }, [3] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .callback = &uplcom_read_clear_stall_callback, .timeout = 1000, /* 1 second */ }, [4] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .callback = &uplcom_set_line_state_callback, .timeout = 1000, /* 1 second */ }, [5] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .callback = &uplcom_set_break_callback, .timeout = 1000, /* 1 second */ }, [6] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t) + sizeof(usb_cdc_line_state_t), .callback = &uplcom_set_line_coding_callback, .timeout = 1000, /* 1 second */ }, }; static const struct usbd_config uplcom_config_intr[UPLCOM_N_INTR_TRANSFER] = { [0] = { .type = UE_INTERRUPT, .endpoint = -1, /* any */ .direction = UE_DIR_IN, .flags = USBD_SHORT_XFER_OK, .bufsize = 0, /* use wMaxPacketSize */ .callback = &uplcom_intr_callback, }, [1] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .callback = &uplcom_intr_clear_stall_callback, .timeout = 1000, /* 1 second */ }, }; struct ucom_callback uplcom_callback = { .ucom_get_status = &uplcom_get_status, .ucom_set_dtr = &uplcom_set_dtr, .ucom_set_rts = &uplcom_set_rts, .ucom_set_break = &uplcom_set_break, .ucom_param = &uplcom_param, .ucom_open = &uplcom_open, .ucom_close = &uplcom_close, .ucom_ioctl = &uplcom_ioctl, .ucom_start_read = &uplcom_start_read, .ucom_stop_read = &uplcom_stop_read, .ucom_start_write = &uplcom_start_write, .ucom_stop_write = &uplcom_stop_write, }; static const struct uplcom_product { u_int16_t vendor; u_int16_t product; u_int16_t release; /* the highest release value accepted, * 0xFFFF means any value, first match * wins */ u_int8_t chiptype; } uplcom_products [] = { /* I/O DATA USB-RSAQ */ { USB_VENDOR_IODATA, USB_PRODUCT_IODATA_USBRSAQ, 0xFFFF, TYPE_PL2303 }, /* I/O DATA USB-RSAQ2 */ { USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_RSAQ2, 0xFFFF, TYPE_PL2303 }, /* I/O DATA USB-RSAQ3 */ { USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_RSAQ3, 0xFFFF, TYPE_PL2303X }, /* PLANEX USB-RS232 URS-03 */ { USB_VENDOR_ATEN, USB_PRODUCT_ATEN_UC232A, 0xFFFF, TYPE_PL2303 }, /* ST Lab USB-SERIAL-4 */ { USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2303, 0x0300, TYPE_PL2303X }, /* IOGEAR/ATEN UC-232A (also ST Lab USB-SERIAL-1) */ { USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2303, 0xFFFF, TYPE_PL2303 }, /* TDK USB-PHS Adapter UHA6400 */ { USB_VENDOR_TDK, USB_PRODUCT_TDK_UHA6400, 0xFFFF, TYPE_PL2303 }, /* RATOC REX-USB60 */ { USB_VENDOR_RATOC, USB_PRODUCT_RATOC_REXUSB60, 0xFFFF, TYPE_PL2303 }, /* ELECOM UC-SGT */ { USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_UCSGT, 0xFFFF, TYPE_PL2303 }, { USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_UCSGT0, 0xFFFF, TYPE_PL2303 }, /* Sony Ericsson USB Cable */ { USB_VENDOR_SONYERICSSON, USB_PRODUCT_SONYERICSSON_DCU10, 0xFFFF,TYPE_PL2303 }, /* SOURCENEXT KeikaiDenwa 8 */ { USB_VENDOR_SOURCENEXT, USB_PRODUCT_SOURCENEXT_KEIKAI8, 0xFFFF, TYPE_PL2303 }, /* SOURCENEXT KeikaiDenwa 8 with charger */ { USB_VENDOR_SOURCENEXT, USB_PRODUCT_SOURCENEXT_KEIKAI8_CHG, 0xFFFF, TYPE_PL2303 }, /* HAL Corporation Crossam2+USB */ { USB_VENDOR_HAL, USB_PRODUCT_HAL_IMR001, 0xFFFF, TYPE_PL2303 }, /* Sitecom USB to Serial */ { USB_VENDOR_SITECOM, USB_PRODUCT_SITECOM_SERIAL, 0xFFFF, TYPE_PL2303 }, /* Tripp-Lite U209-000-R */ { USB_VENDOR_TRIPPLITE, USB_PRODUCT_TRIPPLITE_U209, 0xFFFF, TYPE_PL2303X }, { 0, 0 } }; static device_method_t uplcom_methods[] = { DEVMETHOD(device_probe, uplcom_probe), DEVMETHOD(device_attach, uplcom_attach), DEVMETHOD(device_detach, uplcom_detach), { 0, 0 } }; static devclass_t uplcom_devclass; static driver_t uplcom_driver = { .name = "uplcom", .methods = uplcom_methods, .size = sizeof (struct uplcom_softc), }; DRIVER_MODULE(uplcom, uhub, uplcom_driver, uplcom_devclass, usbd_driver_load, 0); MODULE_DEPEND(uplcom, usb, 1, 1, 1); MODULE_DEPEND(uplcom, ucom, UCOM_MINVER, UCOM_PREFVER, UCOM_MAXVER); MODULE_VERSION(uplcom, UPLCOM_MODVER); static const struct uplcom_product * uplcom_find_up(struct usb_attach_arg *uaa) { const struct uplcom_product *up = uplcom_products; if (uaa->iface == NULL) { while(up->vendor) { if ((up->vendor == uaa->vendor) && (up->product == uaa->product) && ((up->release <= uaa->release) || (up->release == 0xFFFF))) { return up; } up++; } } return NULL; } static int uplcom_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); DPRINTF(10, "\n"); return (uplcom_find_up(uaa) ? UMATCH_VENDOR_PRODUCT : UMATCH_NONE); } static int uplcom_attach(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); const struct uplcom_product *up = uplcom_find_up(uaa); struct uplcom_softc *sc = device_get_softc(dev); struct usbd_interface *iface; usb_interface_descriptor_t *id; int error; DPRINTF(10, "\n"); if (sc == NULL) { return ENOMEM; } usbd_set_desc(dev, uaa->device); DPRINTF(0, "sc = %p\n", sc); sc->sc_chiptype = up->chiptype; DPRINTF(0, "chiptype: %s\n", (sc->sc_chiptype == TYPE_PL2303X) ? "2303X" : "2303"); /* configure the chip */ error = usbd_set_config_index(uaa->device, UPLCOM_CONFIG_INDEX, 1); if (error) { device_printf(dev, "failed to set configuration, " "error=%s\n", usbd_errstr(error)); goto detach; } /* * USB-RSAQ1 has two interface * * USB-RSAQ1 | USB-RSAQ2 * -----------------+----------------- * Interface 0 |Interface 0 * Interrupt(0x81) | Interrupt(0x81) * -----------------+ BulkIN(0x02) * Interface 1 | BulkOUT(0x83) * BulkIN(0x02) | * BulkOUT(0x83) | */ iface = usbd_get_iface(uaa->device, UPLCOM_IFACE_INDEX); if (iface == NULL) { DPRINTF(0, "no interface (1)!\n"); goto detach; } id = usbd_get_interface_descriptor(iface); if (id == NULL) { DPRINTF(0, "no interface descriptor (1)!\n"); goto detach; } sc->sc_ctrl_iface_no = id->bInterfaceNumber; sc->sc_ctrl_iface_index = UPLCOM_IFACE_INDEX; iface = usbd_get_iface(uaa->device, UPLCOM_SECOND_IFACE_INDEX); if (iface) { id = usbd_get_interface_descriptor(iface); if (id == NULL) { device_printf(dev, "no interface descriptor (2)!\n"); goto detach; } sc->sc_data_iface_no = id->bInterfaceNumber; sc->sc_data_iface_index = UPLCOM_SECOND_IFACE_INDEX; } else { sc->sc_data_iface_no = sc->sc_ctrl_iface_no; sc->sc_data_iface_index = sc->sc_ctrl_iface_index; } error = usbd_transfer_setup(uaa->device, sc->sc_data_iface_index, sc->sc_xfer_data, uplcom_config_data, UPLCOM_N_DATA_TRANSFER, sc, &Giant); if (error) { DPRINTF(0, "one or more missing data " "pipes, error=%s\n", usbd_errstr(error)); goto detach; } error = usbd_transfer_setup(uaa->device, sc->sc_ctrl_iface_index, sc->sc_xfer_intr, uplcom_config_intr, UPLCOM_N_INTR_TRANSFER, sc, &Giant); if (error) { DPRINTF(0, "no interrupt pipe, error=%s\n", usbd_errstr(error)); goto detach; } sc->sc_dtr = -1; sc->sc_rts = -1; sc->sc_break = -1; error = uplcom_reset(sc, uaa->device); if (error) { device_printf(dev, "reset failed, error=%s\n", usbd_errstr(error)); goto detach; } error = ucom_attach(&(sc->sc_ucom), 1, sc, &uplcom_callback, &Giant); if (error) { goto detach; } /* do the initialization during attach * so that the system does not sleep * during open: */ if (sc->sc_chiptype == TYPE_PL2303X) { if (uplcom_pl2303x_init(uaa->device)) { device_printf(dev, "init failed!\n"); goto detach; } } return 0; detach: uplcom_detach(dev); return ENXIO; } static int uplcom_detach(device_t dev) { struct uplcom_softc *sc = device_get_softc(dev); DPRINTF(0, "sc=%p\n", sc); ucom_detach(&(sc->sc_ucom), 1); usbd_transfer_unsetup(sc->sc_xfer_intr, UPLCOM_N_INTR_TRANSFER); usbd_transfer_unsetup(sc->sc_xfer_data, UPLCOM_N_DATA_TRANSFER); return 0; } static usbd_status uplcom_reset(struct uplcom_softc *sc, struct usbd_device *udev) { usb_device_request_t req; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = UPLCOM_SET_REQUEST; USETW(req.wValue, 0); USETW(req.wIndex, sc->sc_data_iface_no); USETW(req.wLength, 0); return usbd_do_request(udev, &req, NULL); } struct pl2303x_init { uint8_t req_type; uint8_t request; uint16_t value; uint16_t index; uint16_t length; }; static const struct pl2303x_init pl2303x[] = { { UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8484, 0, 0 }, { UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x0404, 0, 0 }, { UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8484, 0, 0 }, { UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8383, 0, 0 }, { UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8484, 0, 0 }, { UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x0404, 1, 0 }, { UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8484, 0, 0 }, { UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8383, 0, 0 }, { UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0, 1, 0 }, { UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 1, 0, 0 }, { UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 2, 0x44, 0 } }; #define N_PL2302X_INIT (sizeof(pl2303x)/sizeof(pl2303x[0])) static int uplcom_pl2303x_init(struct usbd_device *udev) { usb_device_request_t req; usbd_status err; int i; for (i = 0; i < N_PL2302X_INIT; i++) { req.bmRequestType = pl2303x[i].req_type; req.bRequest = pl2303x[i].request; USETW(req.wValue, pl2303x[i].value); USETW(req.wIndex, pl2303x[i].index); USETW(req.wLength, pl2303x[i].length); err = usbd_do_request(udev, &req, NULL); if (err) { DPRINTF(0, "error=%s\n", usbd_errstr(err)); return EIO; } } return 0; } static void uplcom_set_dtr(struct ucom_softc *ucom, u_int8_t onoff) { struct uplcom_softc *sc = ucom->sc_parent; DPRINTF(0, "onoff = %d\n", onoff); if (sc->sc_dtr != onoff) { sc->sc_dtr = onoff; sc->sc_flag |= UPLCOM_FLAG_SET_LS; usbd_transfer_start(sc->sc_xfer_data[4]); } return; } static void uplcom_set_rts(struct ucom_softc *ucom, u_int8_t onoff) { struct uplcom_softc *sc = ucom->sc_parent; DPRINTF(0, "onoff = %d\n", onoff); if (sc->sc_rts != onoff) { sc->sc_rts = onoff; sc->sc_flag |= UPLCOM_FLAG_SET_LS; usbd_transfer_start(sc->sc_xfer_data[4]); } return; } static void uplcom_set_break(struct ucom_softc *ucom, u_int8_t onoff) { struct uplcom_softc *sc = ucom->sc_parent; DPRINTF(0, "onoff = %d\n", onoff); sc->sc_break = onoff; sc->sc_flag |= UPLCOM_FLAG_SET_BREAK; usbd_transfer_start(sc->sc_xfer_data[5]); return; } static const int32_t uplcom_rates[] = { 75, 150, 300, 600, 1200, 1800, 2400, 3600, 4800, 7200, 9600, 14400, 19200, 28800, 38400, 57600, 115200, /* * Higher speeds are probably possible. PL2303X supports up to * 6Mb and can set any rate */ 230400, 460800, 614400, 921600, 1228800 }; #define N_UPLCOM_RATES (sizeof(uplcom_rates)/sizeof(uplcom_rates[0])) static int uplcom_param(struct ucom_softc *ucom, struct termios *t) { struct uplcom_softc *sc = ucom->sc_parent; u_int32_t i; DPRINTF(0, "sc = %p\n", sc); /* check requested baud rate */ for (i = 0; ; i++) { if (i < N_UPLCOM_RATES) { if (uplcom_rates[i] == t->c_ospeed) { break; } } else { DPRINTF(0, "invalid baud rate (%d)\n", t->c_ospeed); return EIO; } } USETDW(sc->sc_line_state.dwDTERate, t->c_ospeed); sc->sc_line_state.bCharFormat = (t->c_cflag & CSTOPB) ? UCDC_STOP_BIT_2 : UCDC_STOP_BIT_1; sc->sc_line_state.bParityType = (t->c_cflag & PARENB) ? ((t->c_cflag & PARODD) ? UCDC_PARITY_ODD : UCDC_PARITY_EVEN) : UCDC_PARITY_NONE; switch (t->c_cflag & CSIZE) { case CS5: sc->sc_line_state.bDataBits = 5; break; case CS6: sc->sc_line_state.bDataBits = 6; break; case CS7: sc->sc_line_state.bDataBits = 7; break; case CS8: sc->sc_line_state.bDataBits = 8; break; } sc->sc_crtscts = (t->c_cflag & CRTSCTS) ? 1 : 0; sc->sc_flag |= (UPLCOM_FLAG_SET_LC|UPLCOM_FLAG_SET_CRTSCTS); usbd_transfer_start(sc->sc_xfer_data[6]); return 0; } static int uplcom_open(struct ucom_softc *ucom) { struct uplcom_softc *sc = ucom->sc_parent; DPRINTF(0, "sc=%p\n", sc); /* clear stall first */ sc->sc_flag |= (UPLCOM_FLAG_READ_STALL| UPLCOM_FLAG_WRITE_STALL); usbd_transfer_start(sc->sc_xfer_intr[0]); return (0); } static void uplcom_close(struct ucom_softc *ucom) { struct uplcom_softc *sc = ucom->sc_parent; DPRINTF(0, "sc=%p\n", sc); usbd_transfer_stop(sc->sc_xfer_intr[0]); return; } static void uplcom_start_read(struct ucom_softc *ucom) { struct uplcom_softc *sc = ucom->sc_parent; usbd_transfer_start(sc->sc_xfer_data[1]); return; } static void uplcom_stop_read(struct ucom_softc *ucom) { struct uplcom_softc *sc = ucom->sc_parent; usbd_transfer_stop(sc->sc_xfer_data[3]); usbd_transfer_stop(sc->sc_xfer_data[1]); return; } static void uplcom_start_write(struct ucom_softc *ucom) { struct uplcom_softc *sc = ucom->sc_parent; usbd_transfer_start(sc->sc_xfer_data[0]); return; } static void uplcom_stop_write(struct ucom_softc *ucom) { struct uplcom_softc *sc = ucom->sc_parent; usbd_transfer_stop(sc->sc_xfer_data[2]); usbd_transfer_stop(sc->sc_xfer_data[0]); return; } static void uplcom_get_status(struct ucom_softc *ucom, u_int8_t *lsr, u_int8_t *msr) { struct uplcom_softc *sc = ucom->sc_parent; DPRINTF(0, "\n"); if (lsr) { *lsr = sc->sc_lsr; } if (msr) { *msr = sc->sc_msr; } return; } static int uplcom_ioctl(struct ucom_softc *ucom, uint32_t cmd, caddr_t data, int flag, struct thread *td) { int error = ENOTTY; /* TODO: */ DPRINTF(0, "cmd = 0x%08x\n", cmd); switch (cmd) { case TIOCNOTTY: case TIOCMGET: case TIOCMSET: case USB_GET_CM_OVER_DATA: case USB_SET_CM_OVER_DATA: break; default: DPRINTF(0, "unknown command\n"); error = ENOTTY; break; } return error; } static void uplcom_set_line_state_callback(struct usbd_xfer *xfer) { usb_device_request_t *req = xfer->buffer; struct uplcom_softc *sc = xfer->priv_sc; u_int16_t temp; USBD_CHECK_STATUS(xfer); tr_error: DPRINTF(0, "error=%s\n", usbd_errstr(xfer->error)); tr_setup: tr_transferred: if (sc->sc_flag & UPLCOM_FLAG_SET_LS) { sc->sc_flag &= ~UPLCOM_FLAG_SET_LS; temp = ((sc->sc_dtr ? UCDC_LINE_DTR : 0) | (sc->sc_rts ? UCDC_LINE_RTS : 0)); req->bmRequestType = UT_WRITE_CLASS_INTERFACE; req->bRequest = UCDC_SET_CONTROL_LINE_STATE; USETW(req->wValue, temp); USETW(req->wIndex, sc->sc_data_iface_no); USETW(req->wLength, 0); usbd_start_hardware(xfer); } return; } static void uplcom_set_break_callback(struct usbd_xfer *xfer) { usb_device_request_t *req = xfer->buffer; struct uplcom_softc *sc = xfer->priv_sc; u_int16_t temp; USBD_CHECK_STATUS(xfer); tr_error: DPRINTF(0, "error=%s\n", usbd_errstr(xfer->error)); tr_setup: tr_transferred: if (sc->sc_flag & UPLCOM_FLAG_SET_BREAK) { sc->sc_flag &= ~UPLCOM_FLAG_SET_BREAK; temp = (sc->sc_break ? UCDC_BREAK_ON : UCDC_BREAK_OFF); req->bmRequestType = UT_WRITE_CLASS_INTERFACE; req->bRequest = UCDC_SEND_BREAK; USETW(req->wValue, temp); USETW(req->wIndex, sc->sc_data_iface_no); USETW(req->wLength, 0); usbd_start_hardware(xfer); } return; } static void uplcom_set_line_coding_callback(struct usbd_xfer *xfer) { usb_device_request_t *req = xfer->buffer; usb_cdc_line_state_t *ls = (void *)(req+1); struct uplcom_softc *sc = xfer->priv_sc; USBD_CHECK_STATUS(xfer); tr_error: DPRINTF(0, "error=%s\n", usbd_errstr(xfer->error)); tr_setup: tr_transferred: if (sc->sc_flag & UPLCOM_FLAG_SET_LC) { sc->sc_flag &= ~UPLCOM_FLAG_SET_LC; DPRINTF(0, "rate=%d fmt=%d parity=%d bits=%d\n", UGETDW(ls->dwDTERate), ls->bCharFormat, ls->bParityType, ls->bDataBits); req->bmRequestType = UT_WRITE_CLASS_INTERFACE; req->bRequest = UCDC_SET_LINE_CODING; USETW(req->wValue, 0); USETW(req->wIndex, sc->sc_data_iface_no); USETW(req->wLength, UCDC_LINE_STATE_LENGTH); *ls = sc->sc_line_state; xfer->length = (sizeof(*req) + sizeof(*ls)); usbd_start_hardware(xfer); return; } if (sc->sc_flag & UPLCOM_FLAG_SET_CRTSCTS) { sc->sc_flag &= ~UPLCOM_FLAG_SET_CRTSCTS; if (sc->sc_crtscts) { DPRINTF(0, "crtscts = on\n"); req->bmRequestType = UT_WRITE_VENDOR_DEVICE; req->bRequest = UPLCOM_SET_REQUEST; USETW(req->wValue, 0); if (sc->sc_chiptype == TYPE_PL2303X) USETW(req->wIndex, UPLCOM_SET_CRTSCTS_PL2303X); else USETW(req->wIndex, UPLCOM_SET_CRTSCTS); USETW(req->wLength, 0); xfer->length = sizeof(*req); usbd_start_hardware(xfer); return; } } return; } static void uplcom_intr_callback(struct usbd_xfer *xfer) { struct uplcom_softc *sc = xfer->priv_sc; u_int8_t *buf = xfer->buffer; USBD_CHECK_STATUS(xfer); tr_error: if (xfer->error != USBD_CANCELLED) { sc->sc_flag |= UPLCOM_FLAG_INTR_STALL; usbd_transfer_start(sc->sc_xfer_intr[1]); } return; tr_transferred: if (xfer->actlen >= 9) { DPRINTF(0, "status = 0x%02x\n", buf[8]); sc->sc_lsr = 0; sc->sc_msr = 0; if (buf[8] & RSAQ_STATUS_CTS) { sc->sc_msr |= SER_CTS; } if (buf[8] & RSAQ_STATUS_DSR) { sc->sc_msr |= SER_DSR; } if (buf[8] & RSAQ_STATUS_DCD) { sc->sc_msr |= SER_DCD; } ucom_status_change(&(sc->sc_ucom)); } tr_setup: if (sc->sc_flag & UPLCOM_FLAG_INTR_STALL) { usbd_transfer_start(sc->sc_xfer_intr[1]); } else { usbd_start_hardware(xfer); } return; } static void uplcom_intr_clear_stall_callback(struct usbd_xfer *xfer) { struct uplcom_softc *sc = xfer->priv_sc; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, sc->sc_xfer_intr[0]); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, sc->sc_xfer_intr[0]); sc->sc_flag &= ~UPLCOM_FLAG_INTR_STALL; usbd_transfer_start(sc->sc_xfer_intr[0]); return; tr_error: sc->sc_flag &= ~UPLCOM_FLAG_INTR_STALL; DPRINTF(0, "clear stall failed, error=%s\n", usbd_errstr(xfer->error)); return; } static void uplcom_write_callback(struct usbd_xfer *xfer) { struct uplcom_softc *sc = xfer->priv_sc; u_int32_t actlen; USBD_CHECK_STATUS(xfer); tr_error: if (xfer->error != USBD_CANCELLED) { sc->sc_flag |= UPLCOM_FLAG_WRITE_STALL; usbd_transfer_start(sc->sc_xfer_data[2]); } return; tr_setup: tr_transferred: if (sc->sc_flag & UPLCOM_FLAG_WRITE_STALL) { usbd_transfer_start(sc->sc_xfer_data[2]); return; } if(ucom_get_data(&(sc->sc_ucom), xfer->buffer, UPLCOM_OBUFSIZE, &actlen)) { xfer->length = actlen; usbd_start_hardware(xfer); } return; } static void uplcom_write_clear_stall_callback(struct usbd_xfer *xfer) { struct uplcom_softc *sc = xfer->priv_sc; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, sc->sc_xfer_data[0]); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, sc->sc_xfer_data[0]); sc->sc_flag &= ~UPLCOM_FLAG_WRITE_STALL; usbd_transfer_start(sc->sc_xfer_data[0]); return; tr_error: sc->sc_flag &= ~UPLCOM_FLAG_WRITE_STALL; DPRINTF(0, "clear stall failed, error=%s\n", usbd_errstr(xfer->error)); return; } static void uplcom_read_callback(struct usbd_xfer *xfer) { struct uplcom_softc *sc = xfer->priv_sc; USBD_CHECK_STATUS(xfer); tr_error: if (xfer->error != USBD_CANCELLED) { sc->sc_flag |= UPLCOM_FLAG_READ_STALL; usbd_transfer_start(sc->sc_xfer_data[3]); } return; tr_transferred: ucom_put_data(&(sc->sc_ucom), xfer->buffer, xfer->actlen); tr_setup: if (sc->sc_flag & UPLCOM_FLAG_READ_STALL) { usbd_transfer_start(sc->sc_xfer_data[3]); } else { usbd_start_hardware(xfer); } return; } static void uplcom_read_clear_stall_callback(struct usbd_xfer *xfer) { struct uplcom_softc *sc = xfer->priv_sc; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, sc->sc_xfer_data[1]); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, sc->sc_xfer_data[1]); sc->sc_flag &= ~UPLCOM_FLAG_READ_STALL; usbd_transfer_start(sc->sc_xfer_data[1]); return; tr_error: sc->sc_flag &= ~UPLCOM_FLAG_READ_STALL; DPRINTF(0, "clear stall failed, error=%s\n", usbd_errstr(xfer->error)); return; } pwcbsd/usb/urio.c000644 000423 000000 00000033636 10551670754 014536 0ustar00luigiwheel000000 000000 /*- * Copyright (c) 2000 Iwasa Kazmi * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * This code is based on ugen.c and ulpt.c developed by Lennart Augustsson. * This code includes software developed by the NetBSD Foundation, Inc. and * its contributors. */ #include __FBSDID("$FreeBSD: src/sys/dev/usb/urio.c,v 1.35 2005/01/06 01:43:29 imp Exp $"); /* * 2000/3/24 added NetBSD/OpenBSD support (from Alex Nemirovsky) * 2000/3/07 use two bulk-pipe handles for read and write (Dirk) * 2000/3/06 change major number(143), and copyright header * some fix for 4.0 (Dirk) * 2000/3/05 codes for FreeBSD 4.x - CURRENT (Thanks to Dirk-Willem van Gulik) * 2000/3/01 remove retry code from urioioctl() * change method of bulk transfer (no interrupt) * 2000/2/28 small fixes for new rio_usb.h * 2000/2/24 first version. */ #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" #include #ifdef USB_DEBUG #define DPRINTF(sc,n,fmt,...) do { \ if (urio_debug > (n)) \ printf("%s:%s: " fmt, (sc)->sc_name, \ __FUNCTION__ ,##__VA_ARGS__); \ } while(0) static int urio_debug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, urio, CTLFLAG_RW, 0, "USB urio"); SYSCTL_INT(_hw_usb_urio, OID_AUTO, debug, CTLFLAG_RW, &urio_debug, 0, "urio debug level"); #else #define DPRINTF(...) /* nop */ #endif #define URIO_T_WR 0 #define URIO_T_RD 1 #define URIO_T_WR_CS 2 #define URIO_T_RD_CS 3 #define URIO_T_MAX 4 #define URIO_BSIZE (1<<12) /* bytes */ #define URIO_IFQ_MAXLEN 2 /* units */ struct urio_softc { struct usb_cdev sc_cdev; struct mtx sc_mtx; device_t sc_dev; struct usbd_device * sc_udev; struct usbd_xfer * sc_xfer[URIO_T_MAX]; u_int8_t sc_flags; #define URIO_FLAG_READ_STALL 0x01 /* read transfer stalled */ #define URIO_FLAG_WRITE_STALL 0x02 /* write transfer stalled */ u_int8_t sc_name[16]; }; /* prototypes */ static device_probe_t urio_probe; static device_attach_t urio_attach; static device_detach_t urio_detach; static void urio_write_callback(struct usbd_xfer *xfer); static void urio_write_clear_stall_callback(struct usbd_xfer *xfer); static void urio_read_callback(struct usbd_xfer *xfer); static void urio_read_clear_stall_callback(struct usbd_xfer *xfer); static void urio_start_read(struct usb_cdev *cdev); static void urio_stop_read(struct usb_cdev *cdev); static void urio_start_write(struct usb_cdev *cdev); static void urio_stop_write(struct usb_cdev *cdev); static int32_t urio_open(struct usb_cdev *cdev, int32_t fflags, int32_t devtype, struct thread *td); static int32_t urio_ioctl(struct usb_cdev *cdev, u_long cmd, caddr_t addr, int32_t fflags, struct thread *td); static const struct usbd_config urio_config[URIO_T_MAX] = { [URIO_T_WR] = { .type = UE_BULK, .endpoint = -1, /* any */ .direction = UE_DIR_OUT, .bufsize = URIO_BSIZE, .flags = 0, .callback = &urio_write_callback, }, [URIO_T_RD] = { .type = UE_BULK, .endpoint = -1, /* any */ .direction = UE_DIR_IN, .bufsize = URIO_BSIZE, .flags = USBD_SHORT_XFER_OK, .callback = &urio_read_callback, }, [URIO_T_WR_CS] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .flags = USBD_USE_DMA, .callback = &urio_write_clear_stall_callback, .timeout = 1000, /* 1 second */ }, [URIO_T_RD_CS] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .flags = USBD_USE_DMA, .callback = &urio_read_clear_stall_callback, .timeout = 1000, /* 1 second */ }, }; static devclass_t urio_devclass; static device_method_t urio_methods[] = { /* Device interface */ DEVMETHOD(device_probe, urio_probe), DEVMETHOD(device_attach, urio_attach), DEVMETHOD(device_detach, urio_detach), { 0, 0 } }; static driver_t urio_driver = { .name = "urio", .methods = urio_methods, .size = sizeof(struct urio_softc), }; DRIVER_MODULE(urio, uhub, urio_driver, urio_devclass, usbd_driver_load, 0); MODULE_DEPEND(urio, usb, 1,1,1); static int urio_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); usb_device_descriptor_t *dd; if (!uaa->iface) return UMATCH_NONE; dd = usbd_get_device_descriptor(uaa->device); if (dd && (((UGETW(dd->idVendor) == USB_VENDOR_DIAMOND) && (UGETW(dd->idProduct) == USB_PRODUCT_DIAMOND_RIO500USB)) || ((UGETW(dd->idVendor) == USB_VENDOR_DIAMOND2) && ((UGETW(dd->idProduct) == USB_PRODUCT_DIAMOND2_RIO600USB) || (UGETW(dd->idProduct) == USB_PRODUCT_DIAMOND2_RIO800USB))))) return UMATCH_VENDOR_PRODUCT; else return UMATCH_NONE; } static int urio_attach(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct urio_softc *sc = device_get_softc(dev); const char * p_buf[2]; int32_t error; char buf_1[16]; if (sc == NULL) { return ENOMEM; } usbd_set_desc(dev, uaa->device); sc->sc_dev = dev; sc->sc_udev = uaa->device; mtx_init(&(sc->sc_mtx), "urio lock", NULL, MTX_DEF|MTX_RECURSE); snprintf(sc->sc_name, sizeof(sc->sc_name), "%s", device_get_nameunit(dev)); error = usbd_transfer_setup(uaa->device, uaa->iface_index, sc->sc_xfer, urio_config, URIO_T_MAX, sc, &(sc->sc_mtx)); if (error) { DPRINTF(sc, 0, "error=%s\n", usbd_errstr(error)) ; goto detach; } snprintf(buf_1, sizeof(buf_1), "urio%d", device_get_unit(dev)); p_buf[0] = buf_1; p_buf[1] = NULL; sc->sc_cdev.sc_start_read = &urio_start_read; sc->sc_cdev.sc_start_write = &urio_start_write; sc->sc_cdev.sc_stop_read = &urio_stop_read; sc->sc_cdev.sc_stop_write = &urio_stop_write; sc->sc_cdev.sc_open = &urio_open; sc->sc_cdev.sc_ioctl = &urio_ioctl; sc->sc_cdev.sc_flags |= (USB_CDEV_FLAG_FWD_SHORT| USB_CDEV_FLAG_WAKEUP_RD_IMMED| USB_CDEV_FLAG_WAKEUP_WR_IMMED); error = usb_cdev_attach(&(sc->sc_cdev), sc, &(sc->sc_mtx), p_buf, UID_ROOT, GID_OPERATOR, 0644, URIO_BSIZE, URIO_IFQ_MAXLEN, URIO_BSIZE, URIO_IFQ_MAXLEN); if (error) { goto detach; } return 0; /* success */ detach: urio_detach(dev); return ENOMEM; /* failure */ } static void urio_write_callback(struct usbd_xfer *xfer) { struct urio_softc *sc = xfer->priv_sc; u_int32_t actlen; USBD_CHECK_STATUS(xfer); tr_transferred: tr_setup: if (sc->sc_flags & URIO_FLAG_WRITE_STALL) { usbd_transfer_start(sc->sc_xfer[URIO_T_WR_CS]); return; } if (usb_cdev_get_data(&(sc->sc_cdev), xfer->buffer, URIO_BSIZE, &actlen, 0)) { xfer->length = actlen; usbd_start_hardware(xfer); } return; tr_error: if (xfer->error != USBD_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= URIO_FLAG_WRITE_STALL; usbd_transfer_start(sc->sc_xfer[URIO_T_WR_CS]); } return; } static void urio_write_clear_stall_callback(struct usbd_xfer *xfer) { struct urio_softc *sc = xfer->priv_sc; struct usbd_xfer *xfer_other = sc->sc_xfer[URIO_T_WR]; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, xfer_other); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, xfer_other); sc->sc_flags &= ~URIO_FLAG_WRITE_STALL; usbd_transfer_start(xfer_other); return; tr_error: /* bomb out */ sc->sc_flags &= ~URIO_FLAG_WRITE_STALL; usb_cdev_get_data_error(&(sc->sc_cdev)); return; } static void urio_read_callback(struct usbd_xfer *xfer) { struct urio_softc *sc = xfer->priv_sc; struct usbd_mbuf *m; USBD_CHECK_STATUS(xfer); tr_transferred: usb_cdev_put_data(&(sc->sc_cdev), xfer->buffer, xfer->actlen, 1); tr_setup: if (sc->sc_flags & URIO_FLAG_READ_STALL) { usbd_transfer_start(sc->sc_xfer[URIO_T_RD_CS]); return; } USBD_IF_POLL(&sc->sc_cdev.sc_rdq_free, m); if (m) { usbd_start_hardware(xfer); } return; tr_error: if (xfer->error != USBD_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= URIO_FLAG_READ_STALL; usbd_transfer_start(sc->sc_xfer[URIO_T_RD_CS]); } return; } static void urio_read_clear_stall_callback(struct usbd_xfer *xfer) { struct urio_softc *sc = xfer->priv_sc; struct usbd_xfer *xfer_other = sc->sc_xfer[URIO_T_RD]; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, xfer_other); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, xfer_other); sc->sc_flags &= ~URIO_FLAG_READ_STALL; usbd_transfer_start(xfer_other); return; tr_error: /* bomb out */ sc->sc_flags &= ~URIO_FLAG_READ_STALL; usb_cdev_put_data_error(&(sc->sc_cdev)); return; } static void urio_start_read(struct usb_cdev *cdev) { struct urio_softc *sc = cdev->sc_priv_ptr; usbd_transfer_start(sc->sc_xfer[URIO_T_RD]); return; } static void urio_stop_read(struct usb_cdev *cdev) { struct urio_softc *sc = cdev->sc_priv_ptr; usbd_transfer_stop(sc->sc_xfer[URIO_T_RD_CS]); usbd_transfer_stop(sc->sc_xfer[URIO_T_RD]); return; } static void urio_start_write(struct usb_cdev *cdev) { struct urio_softc *sc = cdev->sc_priv_ptr; usbd_transfer_start(sc->sc_xfer[URIO_T_WR]); return; } static void urio_stop_write(struct usb_cdev *cdev) { struct urio_softc *sc = cdev->sc_priv_ptr; usbd_transfer_stop(sc->sc_xfer[URIO_T_WR_CS]); usbd_transfer_stop(sc->sc_xfer[URIO_T_WR]); return; } static int32_t urio_open(struct usb_cdev *cdev, int32_t fflags, int32_t devtype, struct thread *td) { struct urio_softc *sc = cdev->sc_priv_ptr; if ((fflags & (FWRITE|FREAD)) != (FWRITE|FREAD)) { return EACCES; } if (fflags & FREAD) { /* clear stall first */ sc->sc_flags |= URIO_FLAG_READ_STALL; } if (fflags & FWRITE) { /* clear stall first */ sc->sc_flags |= URIO_FLAG_WRITE_STALL; } return 0; /* success */ } static int32_t urio_ioctl(struct usb_cdev *cdev, u_long cmd, caddr_t addr, int32_t fflags, struct thread *td) { usb_device_request_t req; struct iovec iov; struct uio uio; struct urio_softc *sc = cdev->sc_priv_ptr; struct RioCommand *rio_cmd; void *ptr = 0; int32_t error = 0; u_int16_t len; u_int8_t requesttype; usb_cdev_unlock(cdev, fflags); switch (cmd) { case RIO_RECV_COMMAND: if (!(fflags & FWRITE)) { error = EPERM; goto done; } rio_cmd = (struct RioCommand *)addr; if (rio_cmd == NULL) { error = EINVAL; goto done; } len = rio_cmd->length; requesttype = rio_cmd->requesttype | UT_READ_VENDOR_DEVICE; DPRINTF(sc, 1, "sending command:reqtype=%0x req=%0x value=%0x index=%0x len=%0x\n", requesttype, rio_cmd->request, rio_cmd->value, rio_cmd->index, len); break; case RIO_SEND_COMMAND: if (!(fflags & FWRITE)) { error = EPERM; goto done; } rio_cmd = (struct RioCommand *)addr; if (rio_cmd == NULL) { error = EINVAL; goto done; } len = rio_cmd->length; requesttype = rio_cmd->requesttype | UT_WRITE_VENDOR_DEVICE; DPRINTF(sc, 1, "sending command:reqtype=%0x req=%0x value=%0x index=%0x len=%0x\n", requesttype, rio_cmd->request, rio_cmd->value, rio_cmd->index, len); break; default: error = EINVAL; goto done; } /* Send rio control message */ req.bmRequestType = requesttype; req.bRequest = rio_cmd->request; USETW(req.wValue, rio_cmd->value); USETW(req.wIndex, rio_cmd->index); USETW(req.wLength, len); if (len > 32767) { error = EINVAL; goto done; } if (len != 0) { iov.iov_base = (caddr_t)rio_cmd->buffer; iov.iov_len = len; uio.uio_iov = &iov; uio.uio_iovcnt = 1; uio.uio_resid = len; uio.uio_offset = 0; uio.uio_segflg = UIO_USERSPACE; uio.uio_rw = ((req.bmRequestType & UT_READ) ? UIO_READ : UIO_WRITE); uio.uio_procp = td; ptr = malloc(len, M_TEMP, M_WAITOK); if (ptr == NULL) { error = ENOMEM; goto done; } if (uio.uio_rw == UIO_WRITE) { error = uiomove(ptr, len, &uio); if (error) { goto done; } } } mtx_lock(cdev->sc_mtx_ptr); error = usbd_do_request_flags_mtx (sc->sc_udev, cdev->sc_mtx_ptr, &req, ptr, 0, NULL, USBD_DEFAULT_TIMEOUT); mtx_unlock(cdev->sc_mtx_ptr); if (error == 0) { if (len != 0) { if (uio.uio_rw == UIO_READ) { error = uiomove(ptr, len, &uio); } } } else { error = EIO; } done: if (ptr) { free(ptr, M_TEMP); } return usb_cdev_lock(cdev, fflags, error); } static int urio_detach(device_t dev) { struct urio_softc *sc = device_get_softc(dev); DPRINTF(sc, 0, "\n"); usb_cdev_detach(&(sc->sc_cdev)); usbd_transfer_unsetup(sc->sc_xfer, URIO_T_MAX); mtx_destroy(&(sc->sc_mtx)); return 0; } pwcbsd/usb/usb.c000644 000423 000000 00000045217 10551670754 014347 0ustar00luigiwheel000000 000000 /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * USB specifications and other documentation can be found at * http://www.usb.org/developers/docs/ and * http://www.usb.org/developers/devclass_docs/ */ #include #include #include #include #include #include #include /* UIO_XXX */ #include #include #include /* FXXX */ #include /* IOR()/IOW()/IORW() */ #include #include #include #include #include #include #include #include __FBSDID("$FreeBSD: src/sys/dev/usb2/usb.c $"); #define DEV2UNIT(d) (minor(d)) #define DEV2BUS(d) (*((struct usbd_bus **)&((d)->si_drv1))) #define USB_DEV_MINOR 255 /* event queue device */ MALLOC_DEFINE(M_USB, "USB", "USB"); MALLOC_DEFINE(M_USBDEV, "USBdev", "USB device"); MALLOC_DEFINE(M_USBHC, "USBHC", "USB host controller"); /* define this unconditionally in case a kernel module is loaded that * has been compiled with debugging options. */ SYSCTL_NODE(_hw, OID_AUTO, usb, CTLFLAG_RW, 0, "USB debugging"); #ifdef USB_DEBUG int usbdebug = 0; SYSCTL_INT(_hw_usb, OID_AUTO, debug, CTLFLAG_RW, &usbdebug, 0, "usb debug level"); /* * 0 - do usual exploration * 1 - do not use timeout exploration * >1 - do no exploration */ int usb_noexplore = 0; #endif #define USB_MAX_EVENTS 100 struct usb_event_wrapper { struct usb_event ue; TAILQ_ENTRY(usb_event_wrapper) next; }; static TAILQ_HEAD(, usb_event_wrapper) usb_events = TAILQ_HEAD_INITIALIZER(usb_events); #ifndef usb_global_lock struct mtx usb_global_lock; #endif static device_probe_t usb_probe; static device_attach_t usb_attach; static device_detach_t usb_detach; /* these variables are protected by "usb_global_lock" */ static int usb_nevents = 0; static struct selinfo usb_selevent; static struct proc *usb_async_proc; /* process that wants USB SIGIO */ static int usb_dev_open = 0; /**/ static const char * const usbrev_str[] = USBREV_STR; /* * usb_discover - explore the device tree from the root * * usb_discover device nodes, kthread */ static void usb_discover(struct usbd_bus *bus) { PRINTFN(2,("\n")); #ifdef USB_DEBUG if(usb_noexplore > 1) { return; } #endif mtx_assert(&usb_global_lock, MA_OWNED); /* check that only one thread is exploring * at a time */ while(bus->is_exploring) { bus->wait_explore = 1; msleep(&bus->wait_explore, &usb_global_lock, PWAIT, "usb wait explore", 0); } bus->is_exploring = 1; while(bus->root_port.device && bus->root_port.device->hub && bus->needs_explore && (bus->wait_explore == 0)) { bus->needs_explore = 0; /* explore the hub * (this call can sleep, * exiting usb_global_lock, * which is actually Giant) */ (bus->root_port.device->hub->explore) (bus->root_port.device); } bus->is_exploring = 0; if(bus->wait_explore) { bus->wait_explore = 0; wakeup(&bus->wait_explore); } return; } static void usb_event_thread(struct usbd_bus *bus) { mtx_lock(&usb_global_lock); while(1) { if(bus->root_port.device == 0) { break; } #ifdef USB_DEBUG if(usb_noexplore < 2) #endif { usb_discover(bus); } #ifdef USB_DEBUG msleep(&bus->needs_explore, &usb_global_lock, PWAIT, "usbevt", usb_noexplore ? 0 : hz * 60); #else msleep(&bus->needs_explore, &usb_global_lock, PWAIT, "usbevt", hz * 60); #endif PRINTFN(2,("woke up\n")); } bus->event_thread = NULL; /* in case parent is waiting for us to exit */ wakeup(bus); mtx_unlock(&usb_global_lock); PRINTF(("exit\n")); kthread_exit(0); return; } void usb_needs_explore(struct usbd_device *udev) { PRINTFN(2,("\n")); mtx_lock(&usb_global_lock); udev->bus->needs_explore = 1; wakeup(&udev->bus->needs_explore); mtx_unlock(&usb_global_lock); return; } u_int8_t usb_driver_added_refcount; void usb_needs_probe_and_attach(void) { struct usbd_bus *bus; devclass_t dc; device_t dev; int max; PRINTFN(2,("\n")); mtx_lock(&usb_global_lock); usb_driver_added_refcount++; dc = devclass_find("usb"); if(dc) { max = devclass_get_maxunit(dc); while(max >= 0) { dev = devclass_get_device(dc, max); if(dev) { bus = device_get_softc(dev); bus->needs_explore = 1; wakeup(&bus->needs_explore); } max--; } } else { printf("%s: \"usb\" devclass not present!\n", __FUNCTION__); } mtx_unlock(&usb_global_lock); return; } static void usb_create_event_thread(struct usbd_bus *bus) { if(usb_kthread_create1((void*)(void*)&usb_event_thread, bus, &bus->event_thread, "%s", device_get_nameunit(bus->bdev))) { device_printf(bus->bdev, "unable to create event thread for\n"); panic("usb_create_event_thread"); } return; } static int usb_event_get_next(struct usb_event *ue) { struct usb_event_wrapper *uew; int err; mtx_lock(&usb_global_lock); uew = TAILQ_FIRST(&usb_events); if(uew == NULL) { usb_nevents = 0; err = 0; } else { *ue = uew->ue; TAILQ_REMOVE(&usb_events, uew, next); free(uew, M_USBDEV); if(usb_nevents) { usb_nevents--; } err = 1; } mtx_unlock(&usb_global_lock); return (err); } static void usb_event_add(int type, struct usb_event *uep) { struct usb_event_wrapper *uew; struct timeval thetime; uew = malloc(sizeof *uew, M_USBDEV, M_WAITOK|M_ZERO); if(uew == NULL) { return; } uew->ue = *uep; uew->ue.ue_type = type; microtime(&thetime); TIMEVAL_TO_TIMESPEC(&thetime, &uew->ue.ue_time); mtx_lock(&usb_global_lock); if(USB_EVENT_IS_DETACH(type)) { struct usb_event_wrapper *uewi, *uewi_next; for (uewi = TAILQ_FIRST(&usb_events); uewi; uewi = uewi_next) { uewi_next = TAILQ_NEXT(uewi, next); if(uewi->ue.u.ue_driver.ue_cookie.cookie == uep->u.ue_device.udi_cookie.cookie) { TAILQ_REMOVE(&usb_events, uewi, next); free(uewi, M_USBDEV); usb_nevents--; uewi_next = TAILQ_FIRST(&usb_events); } } } if(usb_nevents >= USB_MAX_EVENTS) { /* too many queued events, drop an old one */ PRINTF(("event dropped\n")); struct usb_event ue; (void)usb_event_get_next(&ue); } TAILQ_INSERT_TAIL(&usb_events, uew, next); usb_nevents++; wakeup(&usb_events); selwakeuppri(&usb_selevent, PZERO); if(usb_async_proc != NULL) { PROC_LOCK(usb_async_proc); psignal(usb_async_proc, SIGIO); PROC_UNLOCK(usb_async_proc); } mtx_unlock(&usb_global_lock); return; } void usbd_add_dev_event(int type, struct usbd_device *udev) { struct usb_event ue; bzero(&ue, sizeof(ue)); usbd_fill_deviceinfo(udev, &ue.u.ue_device, USB_EVENT_IS_ATTACH(type)); usb_event_add(type, &ue); return; } void usbd_add_drv_event(int type, struct usbd_device *udev, device_t dev) { struct usb_event ue; bzero(&ue, sizeof(ue)); ue.u.ue_driver.ue_cookie = udev->cookie; strncpy(ue.u.ue_driver.ue_devname, device_get_nameunit(dev), sizeof ue.u.ue_driver.ue_devname); usb_event_add(type, &ue); return; } /* called from uhci_pci_attach */ static int usb_probe(device_t dev) { PRINTF(("\n")); return (UMATCH_GENERIC); } extern cdevsw_t usb_cdevsw; static void __usb_attach(device_t dev, struct usbd_bus *bus) { usbd_status err; u_int8_t speed; struct usb_event ue; struct cdev *cdev; PRINTF(("\n")); mtx_assert(&usb_global_lock, MA_OWNED); bus->root_port.power = USB_MAX_POWER; device_printf(bus->bdev, "USB revision %s", usbrev_str[bus->usbrev]); switch (bus->usbrev) { case USBREV_1_0: case USBREV_1_1: speed = USB_SPEED_FULL; break; case USBREV_2_0: speed = USB_SPEED_HIGH; break; default: printf(", not supported\n"); return; } printf("\n"); /* make sure not to use tsleep() if we are cold booting */ if(cold) { bus->use_polling++; } ue.u.ue_ctrlr.ue_bus = device_get_unit(bus->bdev); usb_event_add(USB_EVENT_CTRLR_ATTACH, &ue); err = usbd_new_device(bus->bdev, bus, 0, speed, 0, &bus->root_port); if(!err) { if(bus->root_port.device->hub == NULL) { device_printf(bus->bdev, "root device is not a hub\n"); return; } /* * the USB bus is explored here so that devices, * for example the keyboard, can work during boot */ /* make sure that the bus is explored */ bus->needs_explore = 1; usb_discover(bus); } else { device_printf(bus->bdev, "root hub problem, error=%s\n", usbd_errstr(err)); } if(cold) { bus->use_polling--; } usb_create_event_thread(bus); /* the per controller devices (used for usb_discover) */ /* XXX This is redundant now, but old usbd's will want it */ cdev = make_dev(&usb_cdevsw, device_get_unit(dev), UID_ROOT, GID_OPERATOR, 0660, "usb%d", device_get_unit(dev)); if(cdev) { DEV2BUS(cdev) = bus; } return; } static u_int8_t usb_post_init_called = 0; static int usb_attach(device_t dev) { struct usbd_bus *bus = device_get_softc(dev); mtx_lock(&usb_global_lock); if(usb_post_init_called != 0) { __usb_attach(dev, bus); } mtx_unlock(&usb_global_lock); return 0; /* return success */ } static void usb_post_init(void *arg) { struct usbd_bus *bus; devclass_t dc; device_t dev; int max; int n; mtx_lock(&usb_global_lock); dc = devclass_find("usb"); if(dc) { max = devclass_get_maxunit(dc); for(n = 0; n <= max; n++) { dev = devclass_get_device(dc, n); if(dev) { bus = device_get_softc(dev); __usb_attach(dev, bus); } } } else { printf("%s: \"usb\" devclass not present!\n", __FUNCTION__); } usb_post_init_called = 1; mtx_unlock(&usb_global_lock); return; } SYSINIT(usb_post_init, SI_SUB_PSEUDO, SI_ORDER_ANY, usb_post_init, NULL); static int usb_detach(device_t dev) { struct usbd_bus *bus = device_get_softc(dev); struct usb_event ue; PRINTF(("start\n")); mtx_lock(&usb_global_lock); /* wait for any possible explore calls to finish */ while(bus->is_exploring) { bus->wait_explore = 1; msleep(&bus->wait_explore, &usb_global_lock, PWAIT, "usb wait explore", 0); } if(bus->root_port.device != NULL) { /* free device, but not sub-devices, * hence they are freed by the * caller of this function */ usbd_free_device(&bus->root_port, 0); } /* kill off event thread */ if(bus->event_thread != NULL) { wakeup(&bus->needs_explore); if(msleep(bus, &usb_global_lock, PWAIT, "usbdet", hz * 60)) { device_printf(bus->bdev, "event thread didn't die\n"); } PRINTF(("event thread dead\n")); } mtx_unlock(&usb_global_lock); ue.u.ue_ctrlr.ue_bus = device_get_unit(bus->bdev); usb_event_add(USB_EVENT_CTRLR_DETACH, &ue); mtx_lock(&bus->mtx); if(bus->bdev == dev) { /* need to clear bus->bdev * here so that the parent * detach routine does not * free this device again */ bus->bdev = NULL; } else { device_printf(dev, "unexpected bus->bdev value!\n"); } mtx_unlock(&bus->mtx); return (0); } static int usbopen(struct cdev *dev, int flag, int mode, struct thread *proc) { int error = 0; mtx_lock(&usb_global_lock); if(DEV2UNIT(dev) == USB_DEV_MINOR) { if(usb_dev_open) { error = EBUSY; goto done; } usb_dev_open = 1; usb_async_proc = 0; } else { struct usbd_bus *bus = DEV2BUS(dev); if(bus->root_port.device == NULL) { /* device is beeing detached */ error = EIO; goto done; } } done: mtx_unlock(&usb_global_lock); return (error); } static int usbread(struct cdev *dev, struct uio *uio, int flag) { struct usb_event ue; int error = 0; if(DEV2UNIT(dev) != USB_DEV_MINOR) { return (ENODEV); } if(uio->uio_resid != sizeof(struct usb_event)) { return (EINVAL); } mtx_lock(&usb_global_lock); for(;;) { if(usb_event_get_next(&ue) != 0) { break; } if(flag & IO_NDELAY) { error = EWOULDBLOCK; break; } error = msleep(&usb_events, &usb_global_lock, (PZERO|PCATCH), "usbrea", 0); if(error) { break; } } mtx_unlock(&usb_global_lock); if(!error) { error = uiomove((void *)&ue, uio->uio_resid, uio); } return (error); } static int usbclose(struct cdev *dev, int flag, int mode, struct thread *proc) { if(DEV2UNIT(dev) == USB_DEV_MINOR) { mtx_lock(&usb_global_lock); usb_async_proc = 0; usb_dev_open = 0; mtx_unlock(&usb_global_lock); } return (0); } static int usbioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *p) { int error = 0; mtx_lock(&usb_global_lock); if(DEV2UNIT(dev) == USB_DEV_MINOR) { switch (cmd) { case FIONBIO: /* all handled in the upper FS layer */ break; case FIOASYNC: if(*(int *)data) #if __FreeBSD_version >= 500000 usb_async_proc = p->td_proc; #else usb_async_proc = p; #endif else usb_async_proc = 0; break; default: error = EINVAL; break; } } else { struct usbd_bus *bus = DEV2BUS(dev); if(bus->root_port.device == NULL) { /* detached */ error = EIO; goto done; } switch (cmd) { #if defined(__FreeBSD__) /* this part should be deleted */ case USB_DISCOVER: break; #endif case USB_REQUEST: { struct usb_ctl_request *ur = (void *)data; int len = UGETW(ur->ucr_request.wLength); struct iovec iov; struct uio uio; void *ptr = 0; int addr = ur->ucr_addr; usbd_status err; int error = 0; PRINTF(("USB_REQUEST addr=%d len=%d\n", addr, len)); if((len < 0) || (len > 32768)) { error = EINVAL; goto done; } if((addr < 0) || (addr >= USB_MAX_DEVICES) || (bus->devices[addr] == 0 /* might be checked by usbd_do_request_flags */)) { error = EINVAL; goto done; } if(len != 0) { iov.iov_base = (caddr_t)ur->ucr_data; iov.iov_len = len; uio.uio_iov = &iov; uio.uio_iovcnt = 1; uio.uio_resid = len; uio.uio_offset = 0; uio.uio_segflg = UIO_USERSPACE; uio.uio_rw = ur->ucr_request.bmRequestType & UT_READ ? UIO_READ : UIO_WRITE; uio.uio_procp = p; ptr = malloc(len, M_TEMP, M_WAITOK); if(uio.uio_rw == UIO_WRITE) { error = uiomove(ptr, len, &uio); if(error) { goto ret; } } } err = usbd_do_request_flags (bus->devices[addr], &ur->ucr_request, ptr, ur->ucr_flags, &ur->ucr_actlen, USBD_DEFAULT_TIMEOUT); if(err) { error = EIO; goto ret; } if(len != 0) { if(uio.uio_rw == UIO_READ) { error = uiomove(ptr, len, &uio); if(error) { goto ret; } } } ret: if(ptr) { free(ptr, M_TEMP); } goto done; } case USB_DEVICEINFO: { struct usb_device_info *di = (void *)data; int addr = di->udi_addr; if((addr < 1) || (addr >= USB_MAX_DEVICES)) { error = EINVAL; goto done; } if (bus->devices[addr] == 0) { error = ENXIO; goto done; } error = usbd_fill_deviceinfo(bus->devices[addr], di, 1); goto done; } case USB_DEVICESTATS: *(struct usb_device_stats *)data = bus->stats; break; default: error = EINVAL; break; } } done: mtx_unlock(&usb_global_lock); return (error); } static int usbpoll(struct cdev *dev, int events, struct thread *td) { int revents, mask; int unit = DEV2UNIT(dev); if(unit == USB_DEV_MINOR) { revents = 0; mask = POLLIN | POLLRDNORM; mtx_lock(&usb_global_lock); if((events & mask) && (usb_nevents > 0)) { revents |= events & mask; } if((revents == 0) && (events & mask)) { selrecord(td, &usb_selevent); } mtx_unlock(&usb_global_lock); return (revents); } else { /* select/poll never wakes up - back compat */ return 0; } } cdevsw_t usb_cdevsw = { #ifdef D_VERSION .d_version = D_VERSION, #endif .d_open = usbopen, .d_close = usbclose, .d_read = usbread, .d_ioctl = usbioctl, .d_poll = usbpoll, .d_name = "usb", }; static void usb_init(void *arg) { struct cdev *cdev; #ifndef usb_global_lock mtx_init(&usb_global_lock, "usb_global_lock", NULL, MTX_DEF|MTX_RECURSE); #endif /* the device spitting out events */ cdev = make_dev(&usb_cdevsw, USB_DEV_MINOR, UID_ROOT, GID_OPERATOR, 0660, "usb"); if(cdev) { DEV2BUS(cdev) = NULL; } return; } SYSINIT(usb_init, SI_SUB_DRIVERS, SI_ORDER_FIRST, usb_init, NULL); static devclass_t usb_devclass; static driver_t usb_driver = { .name = "usb", .methods = (device_method_t []) { DEVMETHOD(device_probe, usb_probe), DEVMETHOD(device_attach, usb_attach), DEVMETHOD(device_detach, usb_detach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), {0,0} }, .size = 0, /* the softc must be set by the attacher! */ }; DRIVER_MODULE(usb, ohci, usb_driver, usb_devclass, 0, 0); DRIVER_MODULE(usb, uhci, usb_driver, usb_devclass, 0, 0); DRIVER_MODULE(usb, ehci, usb_driver, usb_devclass, 0, 0); MODULE_DEPEND(usb, usb, 1, 1, 1); MODULE_VERSION(usb, 1); pwcbsd/usb/usb.h000644 000423 000000 00000056411 10551670754 014352 0ustar00luigiwheel000000 000000 /* $NetBSD: usb.h,v 1.69 2002/09/22 23:20:50 augustss Exp $ */ /* $FreeBSD: src/sys/dev/usb/usb.h,v 1.41 2006/09/06 23:44:24 imp Exp $ */ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifndef _USB_H_ #define _USB_H_ #include #include #if defined(__NetBSD__) || defined(__OpenBSD__) #include #endif #if defined(_KERNEL) #if 1 #include #else #include MALLOC_DECLARE(M_USB); MALLOC_DECLARE(M_USBDEV); MALLOC_DECLARE(M_USBHC); #endif #endif /* _KERNEL */ /* These two defines are used by usbd to autoload the usb kld */ #define USB_KLD "usb" /* name of usb module */ #define USB_UHUB "usb/uhub" /* root hub */ #define USB_STACK_VERSION 2 #define USB_HOST_ALIGN 8 /* bytes, must be power of two */ #define USB_MAX_DEVICES 128 #define USB_START_ADDR 0 #define USB_CONTROL_ENDPOINT 0 #define USB_MAX_ENDPOINTS 16 #define USB_FRAMES_PER_SECOND 1000 #ifndef __UA_TYPES_H__ #define __UA_TYPES_H__ /* the following structures are * used to force the compiler to * generate un-aligned memory * access code on processors that * do not support un-aligned * memory accesses: */ struct void_p { void *data; } __packed; struct u_int16_p { u_int16_t data; } __packed; struct u_int32_p { u_int32_t data; } __packed; struct u_int64_p { u_int64_t data; } __packed; typedef struct void_p void_p_t; typedef struct u_int16_p u_int16_p_t; typedef struct u_int32_p u_int32_p_t; typedef struct u_int64_p u_int64_p_t; #endif /* * The USB records contain some unaligned little-endian word * components. The U[SG]ETW macros take care of both the alignment * and endian problem and should always be used to access non-byte * values. */ typedef u_int8_t uByte; typedef u_int8_t uWord[2]; typedef u_int8_t uDWord[4]; #define USETW2(w,h,l) ((w)[0] = (u_int8_t)(l), (w)[1] = (u_int8_t)(h)) #if 1 #define UGETW(w) ((w)[0] | ((w)[1] << 8)) #define USETW(w,v) ((w)[0] = (u_int8_t)(v), (w)[1] = (u_int8_t)((v) >> 8)) #define UGETDW(w) ((w)[0] | ((w)[1] << 8) | ((w)[2] << 16) | ((w)[3] << 24)) #define USETDW(w,v) ((w)[0] = (u_int8_t)(v), \ (w)[1] = (u_int8_t)((v) >> 8), \ (w)[2] = (u_int8_t)((v) >> 16), \ (w)[3] = (u_int8_t)((v) >> 24)) #else /* * On little-endian machines that can handle unanliged accesses * (e.g. i386) these macros can be replaced by the following. */ #define UGETW(w) (*(u_int16_t *)(w)) #define USETW(w,v) (*(u_int16_t *)(w) = (v)) #define UGETDW(w) (*(u_int32_t *)(w)) #define USETDW(w,v) (*(u_int32_t *)(w) = (v)) #endif #if defined(__FreeBSD__) && (__FreeBSD_version <= 500014) #define UPACKED __attribute__ ((packed)) #else #define UPACKED __packed #endif typedef struct { uByte bmRequestType; uByte bRequest; uWord wValue; uWord wIndex; uWord wLength; uByte bData[0]; } UPACKED usb_device_request_t; #define UT_WRITE 0x00 #define UT_READ 0x80 #define UT_STANDARD 0x00 #define UT_CLASS 0x20 #define UT_VENDOR 0x40 #define UT_DEVICE 0x00 #define UT_INTERFACE 0x01 #define UT_ENDPOINT 0x02 #define UT_OTHER 0x03 #define UT_READ_DEVICE (UT_READ | UT_STANDARD | UT_DEVICE) #define UT_READ_INTERFACE (UT_READ | UT_STANDARD | UT_INTERFACE) #define UT_READ_ENDPOINT (UT_READ | UT_STANDARD | UT_ENDPOINT) #define UT_WRITE_DEVICE (UT_WRITE | UT_STANDARD | UT_DEVICE) #define UT_WRITE_INTERFACE (UT_WRITE | UT_STANDARD | UT_INTERFACE) #define UT_WRITE_ENDPOINT (UT_WRITE | UT_STANDARD | UT_ENDPOINT) #define UT_READ_CLASS_DEVICE (UT_READ | UT_CLASS | UT_DEVICE) #define UT_READ_CLASS_INTERFACE (UT_READ | UT_CLASS | UT_INTERFACE) #define UT_READ_CLASS_OTHER (UT_READ | UT_CLASS | UT_OTHER) #define UT_READ_CLASS_ENDPOINT (UT_READ | UT_CLASS | UT_ENDPOINT) #define UT_WRITE_CLASS_DEVICE (UT_WRITE | UT_CLASS | UT_DEVICE) #define UT_WRITE_CLASS_INTERFACE (UT_WRITE | UT_CLASS | UT_INTERFACE) #define UT_WRITE_CLASS_OTHER (UT_WRITE | UT_CLASS | UT_OTHER) #define UT_WRITE_CLASS_ENDPOINT (UT_WRITE | UT_CLASS | UT_ENDPOINT) #define UT_READ_VENDOR_DEVICE (UT_READ | UT_VENDOR | UT_DEVICE) #define UT_READ_VENDOR_INTERFACE (UT_READ | UT_VENDOR | UT_INTERFACE) #define UT_READ_VENDOR_OTHER (UT_READ | UT_VENDOR | UT_OTHER) #define UT_READ_VENDOR_ENDPOINT (UT_READ | UT_VENDOR | UT_ENDPOINT) #define UT_WRITE_VENDOR_DEVICE (UT_WRITE | UT_VENDOR | UT_DEVICE) #define UT_WRITE_VENDOR_INTERFACE (UT_WRITE | UT_VENDOR | UT_INTERFACE) #define UT_WRITE_VENDOR_OTHER (UT_WRITE | UT_VENDOR | UT_OTHER) #define UT_WRITE_VENDOR_ENDPOINT (UT_WRITE | UT_VENDOR | UT_ENDPOINT) /* Requests */ #define UR_GET_STATUS 0x00 #define UR_CLEAR_FEATURE 0x01 #define UR_SET_FEATURE 0x03 #define UR_SET_ADDRESS 0x05 #define UR_GET_DESCRIPTOR 0x06 #define UDESC_DEVICE 0x01 #define UDESC_CONFIG 0x02 #define UDESC_STRING 0x03 #define UDESC_INTERFACE 0x04 #define UDESC_ENDPOINT 0x05 #define UDESC_DEVICE_QUALIFIER 0x06 #define UDESC_OTHER_SPEED_CONFIGURATION 0x07 #define UDESC_INTERFACE_POWER 0x08 #define UDESC_OTG 0x09 #define UDESC_CS_DEVICE 0x21 /* class specific */ #define UDESC_CS_CONFIG 0x22 #define UDESC_CS_STRING 0x23 #define UDESC_CS_INTERFACE 0x24 #define UDESC_CS_ENDPOINT 0x25 #define UDESC_HUB 0x29 #define UR_SET_DESCRIPTOR 0x07 #define UR_GET_CONFIG 0x08 #define UR_SET_CONFIG 0x09 #define UR_GET_INTERFACE 0x0a #define UR_SET_INTERFACE 0x0b #define UR_SYNCH_FRAME 0x0c /* Feature numbers */ #define UF_ENDPOINT_HALT 0 #define UF_DEVICE_REMOTE_WAKEUP 1 #define UF_TEST_MODE 2 #define USB_MAX_IPACKET 8 /* maximum size of the initial packet */ #define USB_2_MAX_CTRL_PACKET 64 #define USB_2_MAX_BULK_PACKET 512 typedef struct { uByte bLength; uByte bDescriptorType; uByte bDescriptorSubtype; } UPACKED usb_descriptor_t; typedef struct { uByte bLength; uByte bDescriptorType; uWord bcdUSB; #define UD_USB_2_0 0x0200 #define UD_IS_USB2(d) (UGETW((d)->bcdUSB) >= UD_USB_2_0) uByte bDeviceClass; uByte bDeviceSubClass; uByte bDeviceProtocol; uByte bMaxPacketSize; /* The fields below are not part of the initial descriptor. */ uWord idVendor; uWord idProduct; uWord bcdDevice; uByte iManufacturer; uByte iProduct; uByte iSerialNumber; uByte bNumConfigurations; } UPACKED usb_device_descriptor_t; #define USB_DEVICE_DESCRIPTOR_SIZE 18 typedef struct { uByte bLength; uByte bDescriptorType; uWord wTotalLength; uByte bNumInterface; uByte bConfigurationValue; uByte iConfiguration; uByte bmAttributes; #define UC_BUS_POWERED 0x80 #define UC_SELF_POWERED 0x40 #define UC_REMOTE_WAKEUP 0x20 uByte bMaxPower; /* max current in 2 mA units */ #define UC_POWER_FACTOR 2 } UPACKED usb_config_descriptor_t; #define USB_CONFIG_DESCRIPTOR_SIZE 9 typedef struct { uByte bLength; uByte bDescriptorType; uByte bInterfaceNumber; uByte bAlternateSetting; uByte bNumEndpoints; uByte bInterfaceClass; uByte bInterfaceSubClass; uByte bInterfaceProtocol; uByte iInterface; } UPACKED usb_interface_descriptor_t; #define USB_INTERFACE_DESCRIPTOR_SIZE 9 typedef struct { uByte bLength; uByte bDescriptorType; uByte bEndpointAddress; #define UE_GET_DIR(a) ((a) & 0x80) #define UE_SET_DIR(a,d) ((a) | (((d)&1) << 7)) #define UE_DIR_IN 0x80 #define UE_DIR_OUT 0x00 #define UE_DIR_ANY 0xff /* for internal use only! */ #define UE_ADDR 0x0f #define UE_ADDR_ANY 0xff /* for internal use only! */ #define UE_GET_ADDR(a) ((a) & UE_ADDR) uByte bmAttributes; #define UE_XFERTYPE 0x03 #define UE_CONTROL 0x00 #define UE_ISOCHRONOUS 0x01 #define UE_BULK 0x02 #define UE_INTERRUPT 0x03 #define UE_BULK_INTR 0xfe /* for internal use only! */ #define UE_TYPE_ANY 0xff /* for internal use only! */ #define UE_GET_XFERTYPE(a) ((a) & UE_XFERTYPE) #define UE_ISO_TYPE 0x0c #define UE_ISO_ASYNC 0x04 #define UE_ISO_ADAPT 0x08 #define UE_ISO_SYNC 0x0c #define UE_GET_ISO_TYPE(a) ((a) & UE_ISO_TYPE) uWord wMaxPacketSize; uByte bInterval; } UPACKED usb_endpoint_descriptor_t; #define USB_ENDPOINT_DESCRIPTOR_SIZE 7 typedef struct { uByte bLength; uByte bDescriptorType; uWord bString[126]; uByte bUnused; } UPACKED usb_string_descriptor_t; #define USB_MAX_STRING_LEN 128 #define USB_LANGUAGE_TABLE 0 /* # of the string language id table */ /* Hub specific request */ #define UR_GET_BUS_STATE 0x02 #define UR_CLEAR_TT_BUFFER 0x08 #define UR_RESET_TT 0x09 #define UR_GET_TT_STATE 0x0a #define UR_STOP_TT 0x0b /* Hub features */ #define UHF_C_HUB_LOCAL_POWER 0 #define UHF_C_HUB_OVER_CURRENT 1 #define UHF_PORT_CONNECTION 0 #define UHF_PORT_ENABLE 1 #define UHF_PORT_SUSPEND 2 #define UHF_PORT_OVER_CURRENT 3 #define UHF_PORT_RESET 4 #define UHF_PORT_POWER 8 #define UHF_PORT_LOW_SPEED 9 #define UHF_C_PORT_CONNECTION 16 #define UHF_C_PORT_ENABLE 17 #define UHF_C_PORT_SUSPEND 18 #define UHF_C_PORT_OVER_CURRENT 19 #define UHF_C_PORT_RESET 20 #define UHF_PORT_TEST 21 #define UHF_PORT_INDICATOR 22 typedef struct { uByte bDescLength; uByte bDescriptorType; uByte bNbrPorts; uWord wHubCharacteristics; #define UHD_PWR 0x0003 #define UHD_PWR_GANGED 0x0000 #define UHD_PWR_INDIVIDUAL 0x0001 #define UHD_PWR_NO_SWITCH 0x0002 #define UHD_COMPOUND 0x0004 #define UHD_OC 0x0018 #define UHD_OC_GLOBAL 0x0000 #define UHD_OC_INDIVIDUAL 0x0008 #define UHD_OC_NONE 0x0010 #define UHD_TT_THINK 0x0060 #define UHD_TT_THINK_8 0x0000 #define UHD_TT_THINK_16 0x0020 #define UHD_TT_THINK_24 0x0040 #define UHD_TT_THINK_32 0x0060 #define UHD_PORT_IND 0x0080 uByte bPwrOn2PwrGood; /* delay in 2 ms units */ #define UHD_PWRON_FACTOR 2 uByte bHubContrCurrent; uByte DeviceRemovable[32]; /* max 255 ports */ #define UHD_NOT_REMOV(desc, i) \ (((desc)->DeviceRemovable[(i)/8] >> ((i) % 8)) & 1) /* deprecated */ uByte PortPowerCtrlMask[1]; } UPACKED usb_hub_descriptor_t; #define USB_HUB_DESCRIPTOR_SIZE 9 /* includes deprecated PortPowerCtrlMask */ typedef struct { uByte bLength; uByte bDescriptorType; uWord bcdUSB; uByte bDeviceClass; uByte bDeviceSubClass; uByte bDeviceProtocol; uByte bMaxPacketSize0; uByte bNumConfigurations; uByte bReserved; } UPACKED usb_device_qualifier_t; #define USB_DEVICE_QUALIFIER_SIZE 10 typedef struct { uByte bLength; uByte bDescriptorType; uByte bmAttributes; #define UOTG_SRP 0x01 #define UOTG_HNP 0x02 } UPACKED usb_otg_descriptor_t; /* OTG feature selectors */ #define UOTG_B_HNP_ENABLE 3 #define UOTG_A_HNP_SUPPORT 4 #define UOTG_A_ALT_HNP_SUPPORT 5 typedef struct { uWord wStatus; /* Device status flags */ #define UDS_SELF_POWERED 0x0001 #define UDS_REMOTE_WAKEUP 0x0002 /* Endpoint status flags */ #define UES_HALT 0x0001 } UPACKED usb_status_t; typedef struct { uWord wHubStatus; #define UHS_LOCAL_POWER 0x0001 #define UHS_OVER_CURRENT 0x0002 uWord wHubChange; } UPACKED usb_hub_status_t; typedef struct { uWord wPortStatus; #define UPS_CURRENT_CONNECT_STATUS 0x0001 #define UPS_PORT_ENABLED 0x0002 #define UPS_SUSPEND 0x0004 #define UPS_OVERCURRENT_INDICATOR 0x0008 #define UPS_RESET 0x0010 #define UPS_PORT_POWER 0x0100 #define UPS_LOW_SPEED 0x0200 #define UPS_HIGH_SPEED 0x0400 #define UPS_PORT_TEST 0x0800 #define UPS_PORT_INDICATOR 0x1000 uWord wPortChange; #define UPS_C_CONNECT_STATUS 0x0001 #define UPS_C_PORT_ENABLED 0x0002 #define UPS_C_SUSPEND 0x0004 #define UPS_C_OVERCURRENT_INDICATOR 0x0008 #define UPS_C_PORT_RESET 0x0010 } UPACKED usb_port_status_t; /* Device class codes */ #define UDCLASS_IN_INTERFACE 0x00 #define UDCLASS_COMM 0x02 #define UDCLASS_HUB 0x09 #define UDSUBCLASS_HUB 0x00 #define UDPROTO_FSHUB 0x00 #define UDPROTO_HSHUBSTT 0x01 #define UDPROTO_HSHUBMTT 0x02 #define UDCLASS_DIAGNOSTIC 0xdc #define UDCLASS_WIRELESS 0xe0 #define UDSUBCLASS_RF 0x01 #define UDPROTO_BLUETOOTH 0x01 #define UDCLASS_VENDOR 0xff /* Interface class codes */ #define UICLASS_UNSPEC 0x00 #define UICLASS_AUDIO 0x01 #define UISUBCLASS_AUDIOCONTROL 1 #define UISUBCLASS_AUDIOSTREAM 2 #define UISUBCLASS_MIDISTREAM 3 #define UICLASS_CDC 0x02 /* communication */ #define UISUBCLASS_DIRECT_LINE_CONTROL_MODEL 1 #define UISUBCLASS_ABSTRACT_CONTROL_MODEL 2 #define UISUBCLASS_TELEPHONE_CONTROL_MODEL 3 #define UISUBCLASS_MULTICHANNEL_CONTROL_MODEL 4 #define UISUBCLASS_CAPI_CONTROLMODEL 5 #define UISUBCLASS_ETHERNET_NETWORKING_CONTROL_MODEL 6 #define UISUBCLASS_ATM_NETWORKING_CONTROL_MODEL 7 #define UIPROTO_CDC_AT 1 #define UICLASS_HID 0x03 #define UISUBCLASS_BOOT 1 #define UIPROTO_BOOT_KEYBOARD 1 #define UICLASS_PHYSICAL 0x05 #define UICLASS_IMAGE 0x06 #define UICLASS_PRINTER 0x07 #define UISUBCLASS_PRINTER 1 #define UIPROTO_PRINTER_UNI 1 #define UIPROTO_PRINTER_BI 2 #define UIPROTO_PRINTER_1284 3 #define UICLASS_MASS 0x08 #define UISUBCLASS_RBC 1 #define UISUBCLASS_SFF8020I 2 #define UISUBCLASS_QIC157 3 #define UISUBCLASS_UFI 4 #define UISUBCLASS_SFF8070I 5 #define UISUBCLASS_SCSI 6 #define UIPROTO_MASS_CBI_I 0 #define UIPROTO_MASS_CBI 1 #define UIPROTO_MASS_BBB_OLD 2 /* Not in the spec anymore */ #define UIPROTO_MASS_BBB 80 /* 'P' for the Iomega Zip drive */ #define UICLASS_HUB 0x09 #define UISUBCLASS_HUB 0 #define UIPROTO_FSHUB 0 #define UIPROTO_HSHUBSTT 0 /* Yes, same as previous */ #define UIPROTO_HSHUBMTT 1 #define UICLASS_CDC_DATA 0x0a #define UISUBCLASS_DATA 0 #define UIPROTO_DATA_ISDNBRI 0x30 /* Physical iface */ #define UIPROTO_DATA_HDLC 0x31 /* HDLC */ #define UIPROTO_DATA_TRANSPARENT 0x32 /* Transparent */ #define UIPROTO_DATA_Q921M 0x50 /* Management for Q921 */ #define UIPROTO_DATA_Q921 0x51 /* Data for Q921 */ #define UIPROTO_DATA_Q921TM 0x52 /* TEI multiplexer for Q921 */ #define UIPROTO_DATA_V42BIS 0x90 /* Data compression */ #define UIPROTO_DATA_Q931 0x91 /* Euro-ISDN */ #define UIPROTO_DATA_V120 0x92 /* V.24 rate adaption */ #define UIPROTO_DATA_CAPI 0x93 /* CAPI 2.0 commands */ #define UIPROTO_DATA_HOST_BASED 0xfd /* Host based driver */ #define UIPROTO_DATA_PUF 0xfe /* see Prot. Unit Func. Desc.*/ #define UIPROTO_DATA_VENDOR 0xff /* Vendor specific */ #define UICLASS_SMARTCARD 0x0b /*#define UICLASS_FIRM_UPD 0x0c*/ #define UICLASS_SECURITY 0x0d #define UICLASS_DIAGNOSTIC 0xdc #define UICLASS_WIRELESS 0xe0 #define UISUBCLASS_RF 0x01 #define UIPROTO_BLUETOOTH 0x01 #define UICLASS_APPL_SPEC 0xfe #define UISUBCLASS_FIRMWARE_DOWNLOAD 1 #define UISUBCLASS_IRDA 2 #define UIPROTO_IRDA 0 #define UICLASS_VENDOR 0xff #define UISUBCLASS_XBOX360_CONTROLLER 0x5d #define UIPROTO_XBOX360_GAMEPAD 0x01 #define USB_HUB_MAX_DEPTH 5 /* * Minimum time a device needs to be powered down to go through * a power cycle. XXX Are these time in the spec? */ #define USB_POWER_DOWN_TIME 200 /* ms */ #define USB_PORT_POWER_DOWN_TIME 100 /* ms */ #if 0 /* These are the values from the spec. */ #define USB_PORT_RESET_DELAY 10 /* ms */ #define USB_PORT_ROOT_RESET_DELAY 50 /* ms */ #define USB_PORT_RESET_RECOVERY 10 /* ms */ #define USB_PORT_POWERUP_DELAY 100 /* ms */ #define USB_SET_ADDRESS_SETTLE 2 /* ms */ #define USB_RESUME_DELAY (20*5) /* ms */ #define USB_RESUME_WAIT 10 /* ms */ #define USB_RESUME_RECOVERY 10 /* ms */ #define USB_EXTRA_POWER_UP_TIME 0 /* ms */ #else /* Allow for marginal (i.e. non-conforming) devices. */ #define USB_PORT_RESET_DELAY 50 /* ms */ #define USB_PORT_ROOT_RESET_DELAY 250 /* ms */ #define USB_PORT_RESET_RECOVERY 250 /* ms */ #define USB_PORT_POWERUP_DELAY 300 /* ms */ #define USB_SET_ADDRESS_SETTLE 10 /* ms */ #define USB_RESUME_DELAY (50*5) /* ms */ #define USB_RESUME_WAIT 50 /* ms */ #define USB_RESUME_RECOVERY 50 /* ms */ #define USB_EXTRA_POWER_UP_TIME 20 /* ms */ #endif #define USB_MIN_POWER 100 /* mA */ #define USB_MAX_POWER 500 /* mA */ #define USB_BUS_RESET_DELAY 100 /* ms XXX?*/ #define USB_UNCONFIG_NO 0 #define USB_UNCONFIG_INDEX (-1) /*---------------------------------------------------------------------------* * ioctl() related stuff *---------------------------------------------------------------------------*/ struct usb_ctl_request { int ucr_addr; usb_device_request_t ucr_request; void *ucr_data; int ucr_flags; #define USBD_SHORT_XFER_OK 0x0004 /* allow short reads */ int ucr_actlen; /* actual length transferred */ }; struct usb_alt_interface { int uai_config_index; int uai_interface_index; int uai_alt_no; }; #define USB_CURRENT_CONFIG_INDEX (-1) #define USB_CURRENT_ALT_INDEX (-1) struct usb_config_desc { int ucd_config_index; usb_config_descriptor_t ucd_desc; }; struct usb_interface_desc { int uid_config_index; int uid_interface_index; int uid_alt_index; usb_interface_descriptor_t uid_desc; }; struct usb_endpoint_desc { int ued_config_index; int ued_interface_index; int ued_alt_index; int ued_endpoint_index; usb_endpoint_descriptor_t ued_desc; }; struct usb_full_desc { int ufd_config_index; u_int ufd_size; u_char *ufd_data; }; struct usb_string_desc { int usd_string_index; int usd_language_id; usb_string_descriptor_t usd_desc; }; struct usb_ctl_report_desc { int ucrd_size; u_char ucrd_data[1024]; /* filled data size will vary */ }; typedef struct { u_int32_t cookie; } usb_event_cookie_t; #define USB_MAX_DEVNAMES 4 #define USB_MAX_DEVNAMELEN 16 struct usb_device_info { u_int8_t udi_bus; u_int8_t udi_addr; /* device address */ usb_event_cookie_t udi_cookie; char udi_product[USB_MAX_STRING_LEN]; char udi_vendor[USB_MAX_STRING_LEN]; char udi_release[8]; u_int16_t udi_productNo; u_int16_t udi_vendorNo; u_int16_t udi_releaseNo; u_int8_t udi_class; u_int8_t udi_subclass; u_int8_t udi_protocol; u_int8_t udi_config; u_int8_t udi_speed; #define USB_SPEED_LOW 1 #define USB_SPEED_FULL 2 #define USB_SPEED_HIGH 3 int udi_power; /* power consumption in mA, 0 if selfpowered */ int udi_nports; char udi_devnames[USB_MAX_DEVNAMES][USB_MAX_DEVNAMELEN]; u_int8_t udi_ports[16];/* hub only: addresses of devices on ports */ #define USB_PORT_ENABLED 0xff #define USB_PORT_SUSPENDED 0xfe #define USB_PORT_POWERED 0xfd #define USB_PORT_DISABLED 0xfc }; struct usb_ctl_report { int ucr_report; u_char ucr_data[1024]; /* filled data size will vary */ }; struct usb_device_stats { u_long uds_requests[4]; /* indexed by transfer type UE_* */ }; /* Events that can be read from /dev/usb */ struct usb_event { int ue_type; #define USB_EVENT_CTRLR_ATTACH 1 #define USB_EVENT_CTRLR_DETACH 2 #define USB_EVENT_DEVICE_ATTACH 3 #define USB_EVENT_DEVICE_DETACH 4 #define USB_EVENT_DRIVER_ATTACH 5 #define USB_EVENT_DRIVER_DETACH 6 #define USB_EVENT_IS_ATTACH(n) ((n) == USB_EVENT_CTRLR_ATTACH || (n) == USB_EVENT_DEVICE_ATTACH || (n) == USB_EVENT_DRIVER_ATTACH) #define USB_EVENT_IS_DETACH(n) ((n) == USB_EVENT_CTRLR_DETACH || (n) == USB_EVENT_DEVICE_DETACH || (n) == USB_EVENT_DRIVER_DETACH) struct timespec ue_time; union { struct { int ue_bus; } ue_ctrlr; struct usb_device_info ue_device; struct { usb_event_cookie_t ue_cookie; char ue_devname[16]; } ue_driver; } u; }; /* USB controller */ #define USB_REQUEST _IOWR('U', 1, struct usb_ctl_request) #define USB_SETDEBUG _IOW ('U', 2, int) #define USB_DISCOVER _IO ('U', 3) #define USB_DEVICEINFO _IOWR('U', 4, struct usb_device_info) #define USB_DEVICESTATS _IOR ('U', 5, struct usb_device_stats) /* Generic HID device */ #define USB_GET_REPORT_DESC _IOR ('U', 21, struct usb_ctl_report_desc) #define USB_SET_IMMED _IOW ('U', 22, int) #define USB_GET_REPORT _IOWR('U', 23, struct usb_ctl_report) #define USB_SET_REPORT _IOW ('U', 24, struct usb_ctl_report) #define USB_GET_REPORT_ID _IOR ('U', 25, int) /* Generic USB device */ #define USB_GET_CONFIG _IOR ('U', 100, int) #define USB_SET_CONFIG _IOW ('U', 101, int) #define USB_GET_ALTINTERFACE _IOWR('U', 102, struct usb_alt_interface) #define USB_SET_ALTINTERFACE _IOWR('U', 103, struct usb_alt_interface) #define USB_GET_NO_ALT _IOWR('U', 104, struct usb_alt_interface) #define USB_GET_DEVICE_DESC _IOR ('U', 105, usb_device_descriptor_t) #define USB_GET_CONFIG_DESC _IOWR('U', 106, struct usb_config_desc) #define USB_GET_INTERFACE_DESC _IOWR('U', 107, struct usb_interface_desc) #define USB_GET_ENDPOINT_DESC _IOWR('U', 108, struct usb_endpoint_desc) #define USB_GET_FULL_DESC _IOWR('U', 109, struct usb_full_desc) #define USB_GET_STRING_DESC _IOWR('U', 110, struct usb_string_desc) #define USB_DO_REQUEST _IOWR('U', 111, struct usb_ctl_request) #define USB_GET_DEVICEINFO _IOR ('U', 112, struct usb_device_info) #define USB_SET_SHORT_XFER _IOW ('U', 113, int) #define USB_SET_TIMEOUT _IOW ('U', 114, int) #define USB_GET_FRAME_SIZE _IOR ('U', 115, int) #define USB_SET_FRAME_SIZE _IOW ('U', 116, int) #define USB_GET_BUFFER_SIZE _IOR ('U', 117, int) #define USB_SET_BUFFER_SIZE _IOW ('U', 118, int) /* Modem device */ #define USB_GET_CM_OVER_DATA _IOR ('U', 130, int) #define USB_SET_CM_OVER_DATA _IOW ('U', 131, int) #ifdef OLD_USB_COMPAT /* from usb_port.h */ #define Static static #define USBDEV(bdev) (bdev) #define USB_DECLARE_DRIVER_INIT(dname, init...) \ Static device_probe_t __CONCAT(dname,_match); \ Static device_attach_t __CONCAT(dname,_attach); \ Static device_detach_t __CONCAT(dname,_detach); \ \ Static devclass_t __CONCAT(dname,_devclass); \ \ Static device_method_t __CONCAT(dname,_methods)[] = { \ DEVMETHOD(device_probe, __CONCAT(dname,_match)), \ DEVMETHOD(device_attach, __CONCAT(dname,_attach)), \ DEVMETHOD(device_detach, __CONCAT(dname,_detach)), \ init, \ {0,0} \ }; \ \ Static driver_t __CONCAT(dname,_driver) = { \ #dname, \ __CONCAT(dname,_methods), \ sizeof(struct __CONCAT(dname,_softc)) \ }; \ MODULE_DEPEND(dname, usb, 1, 1, 1) #define METHODS_NONE {0,0} #define USB_DECLARE_DRIVER(dname) USB_DECLARE_DRIVER_INIT(dname, METHODS_NONE) typedef struct thread *usb_proc_ptr; #endif #endif /* _USB_H_ */ pwcbsd/usb/usb_cdc.h000644 000423 000000 00000014130 10551670754 015153 0ustar00luigiwheel000000 000000 /* $NetBSD: usbcdc.h,v 1.9 2004/10/23 13:24:24 augustss Exp $ */ /* $FreeBSD: src/sys/dev/usb/usbcdc.h,v 1.12 2005/03/01 06:35:04 sobomax Exp $ */ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifndef _USB_CDC_H_ #define _USB_CDC_H_ #define UDESCSUB_CDC_HEADER 0 #define UDESCSUB_CDC_CM 1 /* Call Management */ #define UDESCSUB_CDC_ACM 2 /* Abstract Control Model */ #define UDESCSUB_CDC_DLM 3 /* Direct Line Management */ #define UDESCSUB_CDC_TRF 4 /* Telephone Ringer */ #define UDESCSUB_CDC_TCLSR 5 /* Telephone Call */ #define UDESCSUB_CDC_UNION 6 #define UDESCSUB_CDC_CS 7 /* Country Selection */ #define UDESCSUB_CDC_TOM 8 /* Telephone Operational Modes */ #define UDESCSUB_CDC_USBT 9 /* USB Terminal */ #define UDESCSUB_CDC_NCT 10 #define UDESCSUB_CDC_PUF 11 #define UDESCSUB_CDC_EUF 12 #define UDESCSUB_CDC_MCMF 13 #define UDESCSUB_CDC_CCMF 14 #define UDESCSUB_CDC_ENF 15 #define UDESCSUB_CDC_ANF 16 typedef struct { uByte bLength; uByte bDescriptorType; uByte bDescriptorSubtype; uWord bcdCDC; } __packed usb_cdc_header_descriptor_t; typedef struct { uByte bLength; uByte bDescriptorType; uByte bDescriptorSubtype; uByte bmCapabilities; #define USB_CDC_CM_DOES_CM 0x01 #define USB_CDC_CM_OVER_DATA 0x02 uByte bDataInterface; } __packed usb_cdc_cm_descriptor_t; typedef struct { uByte bLength; uByte bDescriptorType; uByte bDescriptorSubtype; uByte bmCapabilities; #define USB_CDC_ACM_HAS_FEATURE 0x01 #define USB_CDC_ACM_HAS_LINE 0x02 #define USB_CDC_ACM_HAS_BREAK 0x04 #define USB_CDC_ACM_HAS_NETWORK_CONN 0x08 } __packed usb_cdc_acm_descriptor_t; typedef struct { uByte bLength; uByte bDescriptorType; uByte bDescriptorSubtype; uByte bMasterInterface; uByte bSlaveInterface[1]; } __packed usb_cdc_union_descriptor_t; typedef struct { uByte bLength; uByte bDescriptorType; uByte bDescriptorSubtype; uByte iMacAddress; uDWord bmEthernetStatistics; uWord wMaxSegmentSize; uWord wNumberMCFikters; uByte bNumberPowerFilters; } __packed usb_cdc_ethernet_descriptor_t; #define UCDC_SEND_ENCAPSULATED_COMMAND 0x00 #define UCDC_GET_ENCAPSULATED_RESPONSE 0x01 #define UCDC_SET_COMM_FEATURE 0x02 #define UCDC_GET_COMM_FEATURE 0x03 #define UCDC_ABSTRACT_STATE 0x01 #define UCDC_COUNTRY_SETTING 0x02 #define UCDC_CLEAR_COMM_FEATURE 0x04 #define UCDC_SET_LINE_CODING 0x20 #define UCDC_GET_LINE_CODING 0x21 #define UCDC_SET_CONTROL_LINE_STATE 0x22 #define UCDC_LINE_DTR 0x0001 #define UCDC_LINE_RTS 0x0002 #define UCDC_SEND_BREAK 0x23 #define UCDC_BREAK_ON 0xffff #define UCDC_BREAK_OFF 0x0000 typedef struct { uWord wState; #define UCDC_IDLE_SETTING 0x0001 #define UCDC_DATA_MULTIPLEXED 0x0002 } __packed usb_cdc_abstract_state_t; #define UCDC_ABSTRACT_STATE_LENGTH 2 typedef struct { uDWord dwDTERate; uByte bCharFormat; #define UCDC_STOP_BIT_1 0 #define UCDC_STOP_BIT_1_5 1 #define UCDC_STOP_BIT_2 2 uByte bParityType; #define UCDC_PARITY_NONE 0 #define UCDC_PARITY_ODD 1 #define UCDC_PARITY_EVEN 2 #define UCDC_PARITY_MARK 3 #define UCDC_PARITY_SPACE 4 uByte bDataBits; } __packed usb_cdc_line_state_t; #define UCDC_LINE_STATE_LENGTH 7 typedef struct { uByte bmRequestType; #define UCDC_NOTIFICATION 0xa1 uByte bNotification; #define UCDC_N_NETWORK_CONNECTION 0x00 #define UCDC_N_RESPONSE_AVAILABLE 0x01 #define UCDC_N_AUX_JACK_HOOK_STATE 0x08 #define UCDC_N_RING_DETECT 0x09 #define UCDC_N_SERIAL_STATE 0x20 #define UCDC_N_CALL_STATE_CHANGED 0x28 #define UCDC_N_LINE_STATE_CHANGED 0x29 #define UCDC_N_CONNECTION_SPEED_CHANGE 0x2a uWord wValue; uWord wIndex; uWord wLength; uByte data[16]; } __packed usb_cdc_notification_t; #define UCDC_NOTIFICATION_LENGTH 8 /* * Bits set in the SERIAL STATE notifcation (first byte of data) */ #define UCDC_N_SERIAL_OVERRUN 0x40 #define UCDC_N_SERIAL_PARITY 0x20 #define UCDC_N_SERIAL_FRAMING 0x10 #define UCDC_N_SERIAL_RI 0x08 #define UCDC_N_SERIAL_BREAK 0x04 #define UCDC_N_SERIAL_DSR 0x02 #define UCDC_N_SERIAL_DCD 0x01 /* Serial state bit masks */ #define UCDC_MDM_RXCARRIER 0x01 #define UCDC_MDM_TXCARRIER 0x02 #define UCDC_MDM_BREAK 0x04 #define UCDC_MDM_RING 0x08 #define UCDC_MDM_FRAMING_ERR 0x10 #define UCDC_MDM_PARITY_ERR 0x20 #define UCDC_MDM_OVERRUN_ERR 0x40 #endif /* _USB_CDC_H_ */ pwcbsd/usb/usb_cdev.c000644 000423 000000 00000073063 10551670754 015350 0ustar00luigiwheel000000 000000 /*- * Copyright (c) 2006 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * * usb_cdev.c - An abstraction layer for creation of devices under /dev/... * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include __FBSDID("$FreeBSD: src/sys/dev/usb/usb_cdev.c $"); #ifdef USB_DEBUG #define DPRINTF(n,fmt,...) \ do { if (usb_cdev_debug > (n)) { \ printf("%s: " fmt, __FUNCTION__,## __VA_ARGS__); } } while (0) static int usb_cdev_debug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, cdev, CTLFLAG_RW, 0, "USB cdev"); SYSCTL_INT(_hw_usb_cdev, OID_AUTO, debug, CTLFLAG_RW, &usb_cdev_debug, 0, "usb cdev debug level"); #else #define DPRINTF(...) #endif #define DEV2SC(dev) (dev)->si_drv1 extern cdevsw_t usb_cdev_cdevsw; static u_int32_t usb_cdev_get_context(int32_t fflags) { u_int32_t context_bit = 0; if (fflags & FREAD) { context_bit |= (USB_CDEV_FLAG_GONE| USB_CDEV_FLAG_OPEN_READ| USB_CDEV_FLAG_SLEEP_READ| USB_CDEV_FLAG_SLEEP_IOCTL_RD| USB_CDEV_FLAG_WAKEUP_READ| USB_CDEV_FLAG_WAKEUP_IOCTL_RD| USB_CDEV_FLAG_SELECT_READ| USB_CDEV_FLAG_CLOSING_READ| USB_CDEV_FLAG_ERROR_READ| USB_CDEV_FLAG_WRITE_ONLY| 0); } if (fflags & FWRITE) { context_bit |= (USB_CDEV_FLAG_GONE| USB_CDEV_FLAG_FLUSHING_WRITE| USB_CDEV_FLAG_OPEN_WRITE| USB_CDEV_FLAG_SLEEP_WRITE| USB_CDEV_FLAG_SLEEP_IOCTL_WR| USB_CDEV_FLAG_WAKEUP_WRITE| USB_CDEV_FLAG_WAKEUP_IOCTL_WR| USB_CDEV_FLAG_SELECT_WRITE| USB_CDEV_FLAG_CLOSING_WRITE| USB_CDEV_FLAG_ERROR_WRITE| USB_CDEV_FLAG_READ_ONLY| 0); } return context_bit; } static int32_t usb_cdev_exit_context(struct usb_cdev *sc, u_int32_t context_bit, int32_t error) { sc->sc_flags &= ~context_bit; if (context_bit & USB_CDEV_FLAG_SLEEP_READ) { if (sc->sc_flags & (USB_CDEV_FLAG_ERROR_READ| USB_CDEV_FLAG_GONE)) { error = ENXIO; } if (sc->sc_flags & USB_CDEV_FLAG_CLOSING_READ) { wakeup(&(sc->sc_wakeup_close_read)); error = EINTR; } } if (context_bit & USB_CDEV_FLAG_SLEEP_IOCTL_RD) { if (sc->sc_flags & USB_CDEV_FLAG_GONE) { error = ENXIO; } if (sc->sc_flags & USB_CDEV_FLAG_CLOSING_READ) { wakeup(&(sc->sc_wakeup_close_read)); error = EINTR; } } if (context_bit & USB_CDEV_FLAG_SLEEP_WRITE) { if (sc->sc_flags & (USB_CDEV_FLAG_ERROR_WRITE| USB_CDEV_FLAG_GONE)) { error = ENXIO; } if (sc->sc_flags & USB_CDEV_FLAG_CLOSING_WRITE) { wakeup(&(sc->sc_wakeup_close_write)); error = EINTR; } } if (context_bit & USB_CDEV_FLAG_SLEEP_IOCTL_WR) { if (sc->sc_flags & USB_CDEV_FLAG_GONE) { error = ENXIO; } if (sc->sc_flags & USB_CDEV_FLAG_CLOSING_WRITE) { wakeup(&(sc->sc_wakeup_close_write)); error = EINTR; } } return error; } static int32_t usb_cdev_uiomove(struct usb_cdev *sc, u_int32_t context_bit, void *cp, int32_t n, struct uio *uio) { int32_t error; sc->sc_flags |= context_bit; mtx_unlock(sc->sc_mtx_ptr); /* "uiomove()" can sleep so one * needs to make a wrapper, exiting * the mutex and checking things: */ error = uiomove(cp, n, uio); mtx_lock(sc->sc_mtx_ptr); return usb_cdev_exit_context(sc, context_bit, error); } static int32_t usb_cdev_msleep(struct usb_cdev *sc, void *ident, u_int32_t context_bit, u_int32_t timeout) { int32_t error; sc->sc_flags |= context_bit; error = msleep(ident, sc->sc_mtx_ptr, PRIBIO|PCATCH, "usb_cdev_msleep", 0); return usb_cdev_exit_context(sc, context_bit, error); } int32_t usb_cdev_sleep(struct usb_cdev *sc, int32_t fflags, u_int32_t timeout) { u_int32_t context_bit = usb_cdev_get_context(fflags); context_bit &= (USB_CDEV_FLAG_SLEEP_IOCTL_RD| USB_CDEV_FLAG_SLEEP_IOCTL_WR| USB_CDEV_FLAG_WAKEUP_IOCTL_RD| USB_CDEV_FLAG_WAKEUP_IOCTL_WR); return usb_cdev_msleep(sc, &(sc->sc_wakeup_ioctl), context_bit, timeout); } void usb_cdev_wakeup(struct usb_cdev *sc) { if (sc->sc_flags & (USB_CDEV_FLAG_WAKEUP_IOCTL_RD| USB_CDEV_FLAG_WAKEUP_IOCTL_WR)) { sc->sc_flags &= ~(USB_CDEV_FLAG_WAKEUP_IOCTL_RD| USB_CDEV_FLAG_WAKEUP_IOCTL_WR); wakeup(&(sc->sc_wakeup_ioctl)); } return; } void usb_cdev_unlock(struct usb_cdev *sc, int32_t fflags) { u_int32_t context_bit = usb_cdev_get_context(fflags); context_bit &= (USB_CDEV_FLAG_SLEEP_IOCTL_RD| USB_CDEV_FLAG_SLEEP_IOCTL_WR); sc->sc_flags |= context_bit; mtx_unlock(sc->sc_mtx_ptr); return; } int32_t usb_cdev_lock(struct usb_cdev *sc, int32_t fflags, int32_t error) { u_int32_t context_bit = usb_cdev_get_context(fflags); context_bit &= (USB_CDEV_FLAG_SLEEP_IOCTL_RD| USB_CDEV_FLAG_SLEEP_IOCTL_WR); mtx_lock(sc->sc_mtx_ptr); return usb_cdev_exit_context(sc, context_bit, error); } /* * the synchronization part is a little more * complicated, hence there are two modes: * * 1) read only and write only, two threads * 2) read and write, one thread * * the job of the following function is to ensure * that only one thread enters a piece of code at * a time: */ static int32_t usb_cdev_wait_context(struct usb_cdev *sc, u_int32_t context_bit) { u_int32_t temp; int32_t error = 0; if (context_bit == 0) { error = EIO; goto done; } if (sc->sc_flags & context_bit & (USB_CDEV_FLAG_GONE| USB_CDEV_FLAG_CLOSING_READ| USB_CDEV_FLAG_CLOSING_WRITE| USB_CDEV_FLAG_SLEEP_READ| USB_CDEV_FLAG_SLEEP_WRITE| USB_CDEV_FLAG_SLEEP_IOCTL_RD| USB_CDEV_FLAG_SLEEP_IOCTL_WR| 0)) { error = EIO; goto done; } sc->sc_cur_context = context_bit; temp = (context_bit & (USB_CDEV_FLAG_SLEEP_IOCTL_RD| USB_CDEV_FLAG_SLEEP_IOCTL_WR)); /* * if two threads are waiting, the * "sc_cur_context" variable will * ensure that the first waiting * thread will run first ! */ while (sc->sc_flags & (~(context_bit|sc->sc_cur_context)) & (USB_CDEV_FLAG_SLEEP_IOCTL_RD| USB_CDEV_FLAG_SLEEP_IOCTL_WR)) { error = usb_cdev_msleep(sc, &(sc->sc_wakeup_ioctl_rdwr), temp, 0); if (error) { goto done; } } done: return error; } static void usb_cdev_unwait_context(struct usb_cdev *sc, u_int32_t context_bit) { if (sc->sc_flags & (~context_bit) & (USB_CDEV_FLAG_SLEEP_IOCTL_RD| USB_CDEV_FLAG_SLEEP_IOCTL_WR)) { /* * if the "other thread" is waiting, * then wake it up: */ wakeup(&(sc->sc_wakeup_ioctl_rdwr)); } return; } static int32_t usb_cdev_open(struct cdev *dev, int32_t oflags, int32_t devtype, struct thread *td) { struct usb_cdev *sc = DEV2SC(dev); struct usbd_mbuf *m; u_int32_t context_bit = usb_cdev_get_context(oflags); u_int32_t temp; int32_t error = 0; DPRINTF(1, "oflags=0x%08x devtype=0x%08x\n", oflags, devtype); if (sc == NULL) { return EIO; } if (context_bit == 0) { return EINVAL; } mtx_lock(sc->sc_mtx_ptr); /* check if the device is already opened * for the given context or if the * device is gone: */ if (sc->sc_flags & context_bit & (USB_CDEV_FLAG_OPEN_READ| USB_CDEV_FLAG_OPEN_WRITE| USB_CDEV_FLAG_READ_ONLY| USB_CDEV_FLAG_WRITE_ONLY| USB_CDEV_FLAG_GONE)) { DPRINTF(1, "is busy, sc_flags=0x%08x " "context=0x%08x\n", sc->sc_flags, context_bit); error = EBUSY; goto done; } temp = (context_bit & (USB_CDEV_FLAG_OPEN_READ| USB_CDEV_FLAG_OPEN_WRITE)); sc->sc_flags |= temp; error = usb_cdev_wait_context(sc, context_bit); if (error) { sc->sc_flags &= ~temp; goto done; } if (context_bit & USB_CDEV_FLAG_OPEN_READ) { /* reset read queue */ while(1) { USBD_IF_DEQUEUE(&(sc->sc_rdq_used), m); if (m) { USBD_IF_ENQUEUE(&(sc->sc_rdq_free), m); } else { break; } } sc->sc_async_rd = NULL; } if (context_bit & USB_CDEV_FLAG_OPEN_WRITE) { /* reset write queue */ while(1) { USBD_IF_DEQUEUE(&(sc->sc_wrq_used), m); if (m) { USBD_IF_ENQUEUE(&(sc->sc_wrq_free), m); } else { break; } } sc->sc_async_wr = NULL; } sc->sc_last_cdev = dev; sc->sc_first_open = !(sc->sc_flags & (~context_bit) & (USB_CDEV_FLAG_OPEN_READ| USB_CDEV_FLAG_OPEN_WRITE)); error = (sc->sc_open)(sc, oflags, devtype, td); if (error) { sc->sc_flags &= ~temp; } else { if (context_bit & USB_CDEV_FLAG_OPEN_READ) { (sc->sc_start_read)(sc); } } usb_cdev_unwait_context(sc, context_bit); done: mtx_unlock(sc->sc_mtx_ptr); DPRINTF(0, "done, error=%d\n", error); return error; } static int32_t usb_cdev_close(struct cdev *dev, int32_t fflags, int32_t devtype, struct thread *td) { struct usb_cdev *sc = DEV2SC(dev); u_int32_t context_bit = usb_cdev_get_context(fflags); int32_t error; DPRINTF(1, "fflags=0x%08x\n", fflags); if (sc == NULL) { return EIO; } if (context_bit == 0) { return EINVAL; } mtx_lock(sc->sc_mtx_ptr); if (sc->sc_flags & context_bit & (USB_CDEV_FLAG_CLOSING_READ| USB_CDEV_FLAG_CLOSING_WRITE| USB_CDEV_FLAG_FLUSHING_WRITE)) { /* * double close on the same device * can happen during detach */ if (sc->sc_flags & USB_CDEV_FLAG_FLUSHING_WRITE) { sc->sc_flags &= ~USB_CDEV_FLAG_FLUSHING_WRITE; /* the device is gone, kill the flush */ wakeup(&(sc->sc_wakeup_flush)); } goto done; } if (sc->sc_flags & context_bit & USB_CDEV_FLAG_OPEN_READ) { sc->sc_flags |= USB_CDEV_FLAG_CLOSING_READ; /* stop read transfer, if not already stopped */ (sc->sc_stop_read)(sc); /* wakeup sleeping threads */ while (sc->sc_flags & (USB_CDEV_FLAG_SLEEP_READ| USB_CDEV_FLAG_SLEEP_IOCTL_RD)) { if (sc->sc_flags & USB_CDEV_FLAG_WAKEUP_READ) { sc->sc_flags &= ~USB_CDEV_FLAG_WAKEUP_READ; wakeup(&(sc->sc_wakeup_read)); } if (sc->sc_flags & USB_CDEV_FLAG_WAKEUP_IOCTL_RD) { sc->sc_flags &= ~USB_CDEV_FLAG_WAKEUP_IOCTL_RD; wakeup(&(sc->sc_wakeup_ioctl)); } error = msleep(&(sc->sc_wakeup_close_read), sc->sc_mtx_ptr, PRIBIO, "usb_cdev_sync_read", 0); } if (sc->sc_flags & USB_CDEV_FLAG_SELECT_READ) { selwakeup(&(sc->sc_read_sel)); } if (sc->sc_async_rd != NULL) { PROC_LOCK(sc->sc_async_rd); psignal(sc->sc_async_rd, SIGIO); PROC_UNLOCK(sc->sc_async_rd); } sc->sc_async_rd = NULL; sc->sc_flags &= ~(USB_CDEV_FLAG_OPEN_READ| USB_CDEV_FLAG_SLEEP_READ| USB_CDEV_FLAG_WAKEUP_READ| USB_CDEV_FLAG_SELECT_READ| USB_CDEV_FLAG_CLOSING_READ| USB_CDEV_FLAG_ERROR_READ); } if (sc->sc_flags & context_bit & USB_CDEV_FLAG_OPEN_WRITE) { /* * wait for data to * be written to pipe: */ if (!(sc->sc_flags & context_bit & (USB_CDEV_FLAG_GONE| USB_CDEV_FLAG_ERROR_WRITE| USB_CDEV_FLAG_ERROR_READ))) { sc->sc_flags |= USB_CDEV_FLAG_FLUSHING_WRITE; /* start write transfer, if not already started */ (sc->sc_start_write)(sc); while (sc->sc_flags & USB_CDEV_FLAG_FLUSHING_WRITE) { error = msleep(&(sc->sc_wakeup_flush), sc->sc_mtx_ptr, PRIBIO|PCATCH, "usb_cdev_flush", 0); if (error || (sc->sc_flags & context_bit & (USB_CDEV_FLAG_GONE| USB_CDEV_FLAG_ERROR_WRITE| USB_CDEV_FLAG_ERROR_READ))) { /* stop flush on signal from user */ break; } } sc->sc_flags &= ~USB_CDEV_FLAG_FLUSHING_WRITE; } sc->sc_flags |= USB_CDEV_FLAG_CLOSING_WRITE; /* stop write transfer, if not already stopped */ (sc->sc_stop_write)(sc); /* wakeup sleeping threads */ while (sc->sc_flags & (USB_CDEV_FLAG_SLEEP_WRITE| USB_CDEV_FLAG_SLEEP_IOCTL_WR)) { if (sc->sc_flags & USB_CDEV_FLAG_WAKEUP_WRITE) { sc->sc_flags &= ~USB_CDEV_FLAG_WAKEUP_WRITE; wakeup(&(sc->sc_wakeup_write)); } if (sc->sc_flags & USB_CDEV_FLAG_WAKEUP_IOCTL_WR) { sc->sc_flags &= ~USB_CDEV_FLAG_WAKEUP_IOCTL_WR; wakeup(&(sc->sc_wakeup_ioctl)); } if (sc->sc_flags & USB_CDEV_FLAG_FLUSHING_WRITE) { sc->sc_flags &= ~USB_CDEV_FLAG_FLUSHING_WRITE; wakeup(&(sc->sc_wakeup_flush)); } error = msleep(&(sc->sc_wakeup_close_write), sc->sc_mtx_ptr, PRIBIO, "usb_cdev_sync_write", 0); } if (sc->sc_flags & USB_CDEV_FLAG_SELECT_WRITE) { selwakeup(&(sc->sc_write_sel)); } if (sc->sc_async_wr != NULL) { PROC_LOCK(sc->sc_async_wr); psignal(sc->sc_async_wr, SIGIO); PROC_UNLOCK(sc->sc_async_wr); } sc->sc_async_wr = NULL; sc->sc_flags &= ~(USB_CDEV_FLAG_FLUSHING_WRITE| USB_CDEV_FLAG_OPEN_WRITE| USB_CDEV_FLAG_SLEEP_WRITE| USB_CDEV_FLAG_WAKEUP_WRITE| USB_CDEV_FLAG_SELECT_WRITE| USB_CDEV_FLAG_CLOSING_WRITE| USB_CDEV_FLAG_ERROR_WRITE); } if (sc->sc_flags & USB_CDEV_FLAG_GONE) { if (!(sc->sc_flags & (USB_CDEV_FLAG_OPEN_READ| USB_CDEV_FLAG_OPEN_WRITE))) { /* it is safe to detach */ wakeup(&(sc->sc_wakeup_detach)); } } done: mtx_unlock(sc->sc_mtx_ptr); DPRINTF(0, "closed\n"); return 0; } static int32_t usb_cdev_write(struct cdev *dev, struct uio *uio, int32_t flags) { struct usb_cdev *sc = DEV2SC(dev); struct usbd_mbuf *m; int32_t error = 0; int32_t io_len; u_int8_t tr_data = 0; DPRINTF(1, "\n"); if (sc == NULL) { return EIO; } mtx_lock(sc->sc_mtx_ptr); if(sc->sc_flags & (USB_CDEV_FLAG_GONE| USB_CDEV_FLAG_CLOSING_WRITE| USB_CDEV_FLAG_SLEEP_WRITE| USB_CDEV_FLAG_SLEEP_IOCTL_WR| USB_CDEV_FLAG_ERROR_WRITE)) { error = EIO; goto done; } while (uio->uio_resid > 0) { USBD_IF_DEQUEUE(&(sc->sc_wrq_free), m); if (m == NULL) { if (flags & O_NONBLOCK) { if (tr_data) { /* return length before error */ break; } error = EWOULDBLOCK; break; } error = usb_cdev_msleep(sc, &(sc->sc_wakeup_write), (USB_CDEV_FLAG_SLEEP_WRITE| USB_CDEV_FLAG_WAKEUP_WRITE), 0); if (error) { break; } continue; } else { tr_data = 1; } USBD_MBUF_RESET(m); io_len = min(m->cur_data_len, uio->uio_resid); m->cur_data_len = io_len; DPRINTF(1, "transfer %d bytes to %p\n", io_len, m->cur_data_ptr); error = usb_cdev_uiomove(sc, USB_CDEV_FLAG_SLEEP_WRITE, m->cur_data_ptr, io_len, uio); if (error) { USBD_IF_ENQUEUE(&(sc->sc_wrq_free), m); break; } else { USBD_IF_ENQUEUE(&(sc->sc_wrq_used), m); (sc->sc_start_write)(sc); } } done: mtx_unlock(sc->sc_mtx_ptr); return error; } static int32_t usb_cdev_read(struct cdev *dev, struct uio *uio, int flags) { struct usb_cdev *sc = DEV2SC(dev); struct usbd_mbuf *m; int32_t error = 0; int32_t io_len; u_int8_t tr_data = 0; if (sc == NULL) { return EIO; } DPRINTF(1, "\n"); mtx_lock(sc->sc_mtx_ptr); if(sc->sc_flags & (USB_CDEV_FLAG_GONE| USB_CDEV_FLAG_CLOSING_READ| USB_CDEV_FLAG_SLEEP_READ| USB_CDEV_FLAG_SLEEP_IOCTL_RD| USB_CDEV_FLAG_ERROR_READ)) { error = EIO; goto done; } while (uio->uio_resid > 0) { USBD_IF_DEQUEUE(&(sc->sc_rdq_used), m); if (m == NULL) { /* start read transfer, if not already started */ (sc->sc_start_read)(sc); if (flags & O_NONBLOCK) { if (tr_data) { /* return length before error */ break; } error = EWOULDBLOCK; break; } error = usb_cdev_msleep(sc, &(sc->sc_wakeup_read), (USB_CDEV_FLAG_SLEEP_READ| USB_CDEV_FLAG_WAKEUP_READ), 0); if (error) { break; } continue; } else { tr_data = 1; } io_len = min(m->cur_data_len, uio->uio_resid); DPRINTF(1, "transfer %d bytes from %p\n", io_len, m->cur_data_ptr); error = usb_cdev_uiomove(sc, USB_CDEV_FLAG_SLEEP_READ, m->cur_data_ptr, io_len, uio); m->cur_data_len -= io_len; m->cur_data_ptr += io_len; if (m->cur_data_len == 0) { USBD_IF_ENQUEUE(&(sc->sc_rdq_free), m); if (sc->sc_flags & USB_CDEV_FLAG_FWD_SHORT) { /* forward short transfers to userland */ if ((m->cur_data_ptr - m->min_data_ptr) < m->max_data_len) { /* short transfer */ break; } } } else { USBD_IF_PREPEND(&(sc->sc_rdq_used), m); } if (error) { break; } } done: mtx_unlock(sc->sc_mtx_ptr); return error; } static int usb_cdev_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int32_t fflags, struct thread *td) { struct usb_cdev *sc = DEV2SC(dev); u_int32_t context_bit = usb_cdev_get_context(fflags); int32_t error = 0; if (sc == NULL) { return EIO; } DPRINTF(1, "fflags=0x%08x\n", fflags); mtx_lock(sc->sc_mtx_ptr); error = usb_cdev_wait_context(sc, context_bit); if (error) { goto done; } switch(cmd) { case FIONBIO: /* handled by upper FS layer */ break; case FIOASYNC: if(fflags & FREAD) { if (*(int *)addr) { if (sc->sc_async_rd != NULL) { error = EBUSY; break; } sc->sc_async_rd = td->td_proc; } else { sc->sc_async_rd = NULL; } } if(fflags & FWRITE) { if (*(int *)addr) { if (sc->sc_async_wr != NULL) { error = EBUSY; break; } sc->sc_async_wr = td->td_proc; } else { sc->sc_async_wr = NULL; } } break; /* XXX this is not the most general solution */ case TIOCSPGRP: if(fflags & FREAD) { if (sc->sc_async_rd == NULL) { error = EINVAL; break; } if (*(int *)addr != sc->sc_async_rd->p_pgid) { error = EPERM; break; } } if(fflags & FWRITE) { if (sc->sc_async_wr == NULL) { error = EINVAL; break; } if (*(int *)addr != sc->sc_async_wr->p_pgid) { error = EPERM; break; } } break; default: sc->sc_last_cdev = dev; error = (sc->sc_ioctl)(sc, cmd, addr, fflags, td); break; } usb_cdev_unwait_context(sc, context_bit); done: mtx_unlock(sc->sc_mtx_ptr); return error; } static int32_t usb_cdev_poll(struct cdev *dev, int32_t events, struct thread *td) { struct usb_cdev *sc = DEV2SC(dev); struct usbd_mbuf *m; int32_t revents = 0; if (sc == NULL) { return POLLNVAL; } DPRINTF(1, "\n"); mtx_lock(sc->sc_mtx_ptr); if (events & (POLLOUT | POLLWRNORM)) { USBD_IF_POLL(&(sc->sc_wrq_free), m); if (m || (sc->sc_flags & (USB_CDEV_FLAG_GONE| USB_CDEV_FLAG_CLOSING_WRITE| USB_CDEV_FLAG_SLEEP_WRITE| USB_CDEV_FLAG_SLEEP_IOCTL_WR| USB_CDEV_FLAG_ERROR_WRITE))) { revents = events & (POLLOUT | POLLWRNORM); } else { sc->sc_flags |= USB_CDEV_FLAG_SELECT_WRITE; selrecord(td, &(sc->sc_write_sel)); } } if (events & (POLLIN | POLLRDNORM)) { USBD_IF_POLL(&(sc->sc_rdq_used), m); if (m || (sc->sc_flags & (USB_CDEV_FLAG_GONE| USB_CDEV_FLAG_CLOSING_READ| USB_CDEV_FLAG_SLEEP_READ| USB_CDEV_FLAG_SLEEP_IOCTL_RD| USB_CDEV_FLAG_ERROR_READ))) { revents = events & (POLLIN | POLLRDNORM); } else { sc->sc_flags |= USB_CDEV_FLAG_SELECT_READ; selrecord(td, &(sc->sc_read_sel)); } } mtx_unlock(sc->sc_mtx_ptr); return revents; } static int32_t usb_cdev_dummy_open(struct usb_cdev *sc, int32_t fflags, int32_t mode, struct thread *td) { return 0; } static int32_t usb_cdev_dummy_ioctl(struct usb_cdev *sc, u_long cmd, caddr_t addr, int32_t fflags, struct thread *td) { return ENOTTY; } static void usb_cdev_dummy_cmd(struct usb_cdev *sc) { return; } static void usb_cdev_dummy_start_write(struct usb_cdev *sc) { sc->sc_flags &= ~USB_CDEV_FLAG_FLUSHING_WRITE; return; } static u_int8_t minor_table[(1<<16) / 8]; static u_int32_t usb_cdev_alloc_minor(void) { u_int32_t x; mtx_lock(&Giant); x = (1<<16); while(--x) { if (minor_table[x / 8] & (1 << (x % 8))) { continue; } else { minor_table[x / 8] |= (1 << (x % 8)); break; } } x = ((x & 0xFF00) << 8) | (x & 0x00FF); mtx_unlock(&Giant); DPRINTF(0, "x=0x%08x\n", x); return x; } static void usb_cdev_free_minor(u_int32_t x) { DPRINTF(0, "x=0x%08x\n", x); if (x & 0x00FF00) { /* invalid minor */ return; } x = ((x & 0xFF0000) >> 8) | (x & 0x0000FF); mtx_lock(&Giant); minor_table[x / 8] &= ~(1 << (x % 8)); mtx_unlock(&Giant); return; } int32_t usb_cdev_attach(struct usb_cdev *sc, void *priv_sc, struct mtx *priv_mtx, const char **pp_dev, uid_t _uid, gid_t _gid, int _perms, u_int32_t rd_size, u_int16_t rd_packets, u_int32_t wr_size, u_int16_t wr_packets) { struct cdev *cdev; u_int32_t minor; u_int8_t n; /* assumes that "sc->xxx" was zeroed by the caller */ if (sc->sc_open == NULL) { sc->sc_open = &usb_cdev_dummy_open; } if (sc->sc_ioctl == NULL) { sc->sc_ioctl = &usb_cdev_dummy_ioctl; } if (sc->sc_start_read == NULL) { sc->sc_start_read = &usb_cdev_dummy_cmd; } if (sc->sc_stop_read == NULL) { sc->sc_stop_read = &usb_cdev_dummy_cmd; } if (sc->sc_start_write == NULL) { sc->sc_start_write = &usb_cdev_dummy_start_write; } if (sc->sc_stop_write == NULL) { sc->sc_stop_write = &usb_cdev_dummy_cmd; } if (priv_mtx == NULL) { priv_mtx = &Giant; } sc->sc_priv_ptr = priv_sc; sc->sc_mtx_ptr = priv_mtx; sc->sc_rdq_free.ifq_maxlen = rd_packets; sc->sc_rdq_used.ifq_maxlen = rd_packets; sc->sc_wrq_free.ifq_maxlen = wr_packets; sc->sc_wrq_used.ifq_maxlen = wr_packets; sc->sc_rdq_pointer = usbd_alloc_mbufs(M_DEVBUF, &(sc->sc_rdq_free), rd_size, rd_packets); if (sc->sc_rdq_pointer == NULL) { goto detach; } sc->sc_wrq_pointer = usbd_alloc_mbufs(M_DEVBUF, &(sc->sc_wrq_free), wr_size, wr_packets); if (sc->sc_wrq_pointer == NULL) { goto detach; } for (n = 0; n < USB_CDEV_COUNT; n++) { if (pp_dev[n] == NULL) { break; } minor = usb_cdev_alloc_minor(); if (minor == 0) { break; } DPRINTF(1, "making device '%s'\n", pp_dev[n]); cdev = make_dev(&usb_cdev_cdevsw, minor, _uid, _gid, _perms, "%s", pp_dev[n]); sc->sc_cdev[n] = cdev; if (cdev) { DEV2SC(cdev) = sc; } } DPRINTF(1, "attached %p\n", sc); return 0; detach: usb_cdev_detach(sc); return ENOMEM; } void usb_cdev_detach(struct usb_cdev *sc) { struct cdev *cdev; int32_t error; u_int32_t _minor; u_int8_t n; if (sc->sc_mtx_ptr == NULL) { sc->sc_mtx_ptr = &Giant; } mtx_lock(sc->sc_mtx_ptr); sc->sc_flags |= USB_CDEV_FLAG_GONE; mtx_unlock(sc->sc_mtx_ptr); for (n = 0; n < USB_CDEV_COUNT; n++) { cdev = sc->sc_cdev[n]; if (cdev) { if (DEV2SC(cdev) == sc) { (void) usb_cdev_close(cdev, FREAD, 0, NULL); (void) usb_cdev_close(cdev, FWRITE, 0, NULL); } DEV2SC(cdev) = NULL; _minor = minor(cdev); destroy_dev(cdev); usb_cdev_free_minor(_minor); } mtx_lock(sc->sc_mtx_ptr); sc->sc_cdev[n] = NULL; mtx_unlock(sc->sc_mtx_ptr); } /* wait for devices to close */ mtx_lock(sc->sc_mtx_ptr); while (sc->sc_flags & (USB_CDEV_FLAG_OPEN_READ| USB_CDEV_FLAG_OPEN_WRITE)) { error = msleep(&(sc->sc_wakeup_detach), sc->sc_mtx_ptr, PRIBIO, "usb_cdev_sync_detach", 0); } mtx_unlock(sc->sc_mtx_ptr); if (sc->sc_rdq_pointer) { free(sc->sc_rdq_pointer, M_DEVBUF); sc->sc_rdq_pointer = NULL; } if (sc->sc_wrq_pointer) { free(sc->sc_wrq_pointer, M_DEVBUF); sc->sc_wrq_pointer = NULL; } DPRINTF(1, "detached %p\n", sc); return; } /* * what: * 0 - normal operation * 1 - force short packet */ void usb_cdev_put_data(struct usb_cdev *sc, u_int8_t *buf, u_int32_t len, u_int8_t what) { struct usbd_mbuf *m; u_int32_t io_len; while (len || (what == 1)) { USBD_IF_DEQUEUE(&(sc->sc_rdq_free), m); if (m) { USBD_MBUF_RESET(m); io_len = min(len, m->cur_data_len); bcopy(buf, m->cur_data_ptr, io_len); m->cur_data_len = io_len; buf += io_len; len -= io_len; USBD_IF_ENQUEUE(&(sc->sc_rdq_used), m); if ((sc->sc_rdq_used.ifq_len >= ((sc->sc_rdq_used.ifq_maxlen+1)/2)) || (sc->sc_flags & USB_CDEV_FLAG_WAKEUP_RD_IMMED)) { /* buffer is half full */ if (sc->sc_flags & USB_CDEV_FLAG_WAKEUP_READ) { sc->sc_flags &= ~USB_CDEV_FLAG_WAKEUP_READ; wakeup(&(sc->sc_wakeup_read)); } if (sc->sc_flags & USB_CDEV_FLAG_SELECT_READ) { sc->sc_flags &= ~USB_CDEV_FLAG_SELECT_READ; selwakeup(&(sc->sc_read_sel)); } if (sc->sc_async_rd != NULL) { PROC_LOCK(sc->sc_async_rd); psignal(sc->sc_async_rd, SIGIO); PROC_UNLOCK(sc->sc_async_rd); } } if ((len == 0) || (what == 1)) { break; } } else { break; } } return; } void usb_cdev_put_data_error(struct usb_cdev *sc) { sc->sc_flags |= USB_CDEV_FLAG_ERROR_READ; if (sc->sc_flags & USB_CDEV_FLAG_WAKEUP_READ) { sc->sc_flags &= ~USB_CDEV_FLAG_WAKEUP_READ; wakeup(&(sc->sc_wakeup_read)); } if (sc->sc_flags & USB_CDEV_FLAG_SELECT_READ) { sc->sc_flags &= ~USB_CDEV_FLAG_SELECT_READ; selwakeup(&(sc->sc_read_sel)); } if (sc->sc_async_rd != NULL) { PROC_LOCK(sc->sc_async_rd); psignal(sc->sc_async_rd, SIGIO); PROC_UNLOCK(sc->sc_async_rd); } return; } /* * what: * 0 - normal operation * 1 - force only one packet * * returns: * 0 - no more data * 1 - data in buffer */ u_int8_t usb_cdev_get_data(struct usb_cdev *sc, u_int8_t *buf, u_int32_t len, u_int32_t *actlen, u_int8_t what) { struct usbd_mbuf *m; u_int32_t io_len; u_int8_t tr_data = 0; actlen[0] = 0; while(1) { USBD_IF_DEQUEUE(&(sc->sc_wrq_used), m); if (m) { tr_data = 1; io_len = min(len, m->cur_data_len); bcopy(m->cur_data_ptr, buf, io_len); len -= io_len; buf += io_len; actlen[0] += io_len; m->cur_data_ptr += io_len; m->cur_data_len -= io_len; if ((m->cur_data_len == 0) || (what == 1)) { USBD_IF_ENQUEUE(&(sc->sc_wrq_free), m); if ((sc->sc_wrq_free.ifq_len >= ((sc->sc_wrq_free.ifq_maxlen+1)/2)) || (sc->sc_flags & USB_CDEV_FLAG_WAKEUP_WR_IMMED)) { /* buffer is half full */ if (sc->sc_flags & USB_CDEV_FLAG_WAKEUP_WRITE) { sc->sc_flags &= ~USB_CDEV_FLAG_WAKEUP_WRITE; wakeup(&(sc->sc_wakeup_write)); } if (sc->sc_flags & USB_CDEV_FLAG_SELECT_WRITE) { sc->sc_flags &= ~USB_CDEV_FLAG_SELECT_WRITE; selwakeup(&(sc->sc_write_sel)); } if (sc->sc_async_wr != NULL) { PROC_LOCK(sc->sc_async_wr); psignal(sc->sc_async_wr, SIGIO); PROC_UNLOCK(sc->sc_async_wr); } } if (what == 1) { break; } } else { USBD_IF_PREPEND(&(sc->sc_wrq_used), m); } } else { if (tr_data) { /* wait for data to be written out */ break; } if (sc->sc_flags & USB_CDEV_FLAG_FLUSHING_WRITE) { sc->sc_flags &= ~USB_CDEV_FLAG_FLUSHING_WRITE; wakeup(&(sc->sc_wakeup_flush)); } break; } if (len == 0) { break; } } return tr_data; } void usb_cdev_get_data_error(struct usb_cdev *sc) { sc->sc_flags |= USB_CDEV_FLAG_ERROR_WRITE; if (sc->sc_flags & USB_CDEV_FLAG_WAKEUP_WRITE) { sc->sc_flags &= ~USB_CDEV_FLAG_WAKEUP_WRITE; wakeup(&(sc->sc_wakeup_write)); } if (sc->sc_flags & USB_CDEV_FLAG_SELECT_WRITE) { sc->sc_flags &= ~USB_CDEV_FLAG_SELECT_WRITE; selwakeup(&(sc->sc_write_sel)); } if (sc->sc_flags & USB_CDEV_FLAG_FLUSHING_WRITE) { sc->sc_flags &= ~USB_CDEV_FLAG_FLUSHING_WRITE; wakeup(&(sc->sc_wakeup_flush)); } if (sc->sc_async_wr != NULL) { PROC_LOCK(sc->sc_async_wr); psignal(sc->sc_async_wr, SIGIO); PROC_UNLOCK(sc->sc_async_wr); } return; } cdevsw_t usb_cdev_cdevsw = { .d_version = D_VERSION, .d_open = usb_cdev_open, .d_close = usb_cdev_close, .d_read = usb_cdev_read, .d_write = usb_cdev_write, .d_ioctl = usb_cdev_ioctl, .d_poll = usb_cdev_poll, .d_name = "usb_cdev", .d_flags = D_TRACKCLOSE, }; pwcbsd/usb/usb_hid.c000644 000423 000000 00000024246 10551670754 015172 0ustar00luigiwheel000000 000000 /* $NetBSD: hid.c,v 1.17 2001/11/13 06:24:53 lukem Exp $ */ #include __FBSDID("$FreeBSD: src/sys/dev/usb/hid.c,v 1.26 2006/03/22 02:04:12 iedowse Exp $"); /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #ifdef USB_DEBUG #define DPRINTF(x) if (usbdebug) logprintf x #define DPRINTFN(n,x) if (usbdebug>(n)) logprintf x #else #define DPRINTF(x) #define DPRINTFN(n,x) #endif Static void hid_clear_local(struct hid_item *); #define MAXUSAGE 100 struct hid_data { const u_char *start; const u_char *end; const u_char *p; struct hid_item cur; int32_t usages[MAXUSAGE]; int nu; int minset; int multi; int multimax; int kindset; }; Static void hid_clear_local(struct hid_item *c) { c->usage = 0; c->usage_minimum = 0; c->usage_maximum = 0; c->designator_index = 0; c->designator_minimum = 0; c->designator_maximum = 0; c->string_index = 0; c->string_minimum = 0; c->string_maximum = 0; c->set_delimiter = 0; } struct hid_data * hid_start_parse(const void *d, int len, int kindset) { struct hid_data *s; s = malloc(sizeof *s, M_TEMP, M_WAITOK|M_ZERO); s->start = s->p = d; s->end = ((const char *)d) + len; s->kindset = kindset; return (s); } void hid_end_parse(struct hid_data *s) { while (s->cur.next != NULL) { struct hid_item *hi = s->cur.next->next; free(s->cur.next, M_TEMP); s->cur.next = hi; } free(s, M_TEMP); } int hid_get_item(struct hid_data *s, struct hid_item *h) { struct hid_item *c = &s->cur; unsigned int bTag, bType, bSize; u_int32_t oldpos; const u_char *data; int32_t dval; const u_char *p; struct hid_item *hi; int i; top: if (s->multimax != 0) { if (s->multi < s->multimax) { c->usage = s->usages[min(s->multi, s->nu-1)]; s->multi++; *h = *c; c->loc.pos += c->loc.size; h->next = 0; return (1); } else { c->loc.count = s->multimax; s->multimax = 0; s->nu = 0; hid_clear_local(c); } } for (;;) { p = s->p; if (p >= s->end) return (0); bSize = *p++; if (bSize == 0xfe) { /* long item */ bSize = *p++; bSize |= *p++ << 8; bTag = *p++; data = p; p += bSize; bType = 0xff; /* XXX what should it be */ } else { /* short item */ bTag = bSize >> 4; bType = (bSize >> 2) & 3; bSize &= 3; if (bSize == 3) bSize = 4; data = p; p += bSize; } s->p = p; switch(bSize) { case 0: dval = 0; break; case 1: dval = (int8_t)*data++; break; case 2: dval = *data++; dval |= *data++ << 8; dval = (int16_t)dval; break; case 4: dval = *data++; dval |= *data++ << 8; dval |= *data++ << 16; dval |= *data++ << 24; break; default: printf("BAD LENGTH %d\n", bSize); continue; } switch (bType) { case 0: /* Main */ switch (bTag) { case 8: /* Input */ if (!(s->kindset & (1 << hid_input))) continue; c->kind = hid_input; c->flags = dval; ret: if (c->flags & HIO_VARIABLE) { s->multimax = c->loc.count; s->multi = 0; c->loc.count = 1; if (s->minset) { for (i = c->usage_minimum; i <= c->usage_maximum; i++) { s->usages[s->nu] = i; if (s->nu < MAXUSAGE-1) s->nu++; } s->minset = 0; } goto top; } else { *h = *c; h->next = 0; c->loc.pos += c->loc.size * c->loc.count; hid_clear_local(c); s->minset = 0; return (1); } case 9: /* Output */ if (!(s->kindset & (1 << hid_output))) continue; c->kind = hid_output; c->flags = dval; goto ret; case 10: /* Collection */ c->kind = hid_collection; c->collection = dval; c->collevel++; *h = *c; hid_clear_local(c); s->nu = 0; return (1); case 11: /* Feature */ if (!(s->kindset & (1 << hid_feature))) continue; c->kind = hid_feature; c->flags = dval; goto ret; case 12: /* End collection */ c->kind = hid_endcollection; c->collevel--; *h = *c; hid_clear_local(c); s->nu = 0; return (1); default: printf("Main bTag=%d\n", bTag); break; } break; case 1: /* Global */ switch (bTag) { case 0: c->_usage_page = dval << 16; break; case 1: c->logical_minimum = dval; break; case 2: c->logical_maximum = dval; break; case 3: c->physical_maximum = dval; break; case 4: c->physical_maximum = dval; break; case 5: c->unit_exponent = dval; break; case 6: c->unit = dval; break; case 7: c->loc.size = dval; break; case 8: c->report_ID = dval; break; case 9: c->loc.count = dval; break; case 10: /* Push */ hi = malloc(sizeof *hi, M_TEMP, M_WAITOK); *hi = s->cur; c->next = hi; break; case 11: /* Pop */ hi = c->next; oldpos = c->loc.pos; s->cur = *hi; c->loc.pos = oldpos; free(hi, M_TEMP); break; default: printf("Global bTag=%d\n", bTag); break; } break; case 2: /* Local */ switch (bTag) { case 0: if (bSize == 1) dval = c->_usage_page | (dval&0xff); else if (bSize == 2) dval = c->_usage_page | (dval&0xffff); c->usage = dval; if (s->nu < MAXUSAGE) s->usages[s->nu++] = dval; /* else XXX */ break; case 1: s->minset = 1; if (bSize == 1) dval = c->_usage_page | (dval&0xff); else if (bSize == 2) dval = c->_usage_page | (dval&0xffff); c->usage_minimum = dval; break; case 2: if (bSize == 1) dval = c->_usage_page | (dval&0xff); else if (bSize == 2) dval = c->_usage_page | (dval&0xffff); c->usage_maximum = dval; break; case 3: c->designator_index = dval; break; case 4: c->designator_minimum = dval; break; case 5: c->designator_maximum = dval; break; case 7: c->string_index = dval; break; case 8: c->string_minimum = dval; break; case 9: c->string_maximum = dval; break; case 10: c->set_delimiter = dval; break; default: printf("Local bTag=%d\n", bTag); break; } break; default: printf("default bType=%d\n", bType); break; } } } int hid_report_size(const void *buf, int len, enum hid_kind k, u_int8_t *idp) { struct hid_data *d; struct hid_item h; int hi, lo, size, id; id = 0; hi = lo = -1; for (d = hid_start_parse(buf, len, 1<size = 0; return (0); } u_long hid_get_data(const u_char *buf, u_int32_t len, struct hid_location *loc) { u_int hpos = loc->pos; u_int hsize = loc->size; u_int32_t data; int i, s, t; DPRINTFN(10, ("hid_get_data: loc %d/%d\n", hpos, hsize)); if (hsize == 0) return (0); data = 0; s = hpos / 8; for (i = hpos; i < (hpos+hsize); i += 8) { t = (i / 8); if (t < len) { data |= buf[t] << ((t - s) * 8); } } data >>= hpos % 8; data &= (1 << hsize) - 1; hsize = 32 - hsize; /* Sign extend */ data = ((int32_t)data << hsize) >> hsize; DPRINTFN(10,("hid_get_data: loc %d/%d = %lu\n", loc->pos, loc->size, (long)data)); return (data); } int hid_is_collection(const void *desc, int size, u_int32_t usage) { struct hid_data *hd; struct hid_item hi; int err; hd = hid_start_parse(desc, size, hid_input); if (hd == NULL) return (0); err = hid_get_item(hd, &hi) && hi.kind == hid_collection && hi.usage == usage; hid_end_parse(hd); return (err); } pwcbsd/usb/usb_hid.h000644 000423 000000 00000016612 10551670754 015175 0ustar00luigiwheel000000 000000 /* $NetBSD: usbhid.h,v 1.9 2000/09/03 19:09:14 augustss Exp $ */ /* $FreeBSD: src/sys/dev/usb/usbhid.h,v 1.15 2005/01/06 01:43:29 imp Exp $ */ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifndef _USBHID_H_ #define _USBHID_H_ #define UR_GET_HID_DESCRIPTOR 0x06 #define UDESC_HID 0x21 #define UDESC_REPORT 0x22 #define UDESC_PHYSICAL 0x23 #define UR_SET_HID_DESCRIPTOR 0x07 #define UR_GET_REPORT 0x01 #define UR_SET_REPORT 0x09 #define UR_GET_IDLE 0x02 #define UR_SET_IDLE 0x0a #define UR_GET_PROTOCOL 0x03 #define UR_SET_PROTOCOL 0x0b typedef struct usb_hid_descriptor { uByte bLength; uByte bDescriptorType; uWord bcdHID; uByte bCountryCode; uByte bNumDescriptors; struct { uByte bDescriptorType; uWord wDescriptorLength; } descrs[1]; } UPACKED usb_hid_descriptor_t; #define USB_HID_DESCRIPTOR_SIZE(n) (9+((n)*3)) /* Usage pages */ #define HUP_UNDEFINED 0x0000 #define HUP_GENERIC_DESKTOP 0x0001 #define HUP_SIMULATION 0x0002 #define HUP_VR_CONTROLS 0x0003 #define HUP_SPORTS_CONTROLS 0x0004 #define HUP_GAMING_CONTROLS 0x0005 #define HUP_KEYBOARD 0x0007 #define HUP_LEDS 0x0008 #define HUP_BUTTON 0x0009 #define HUP_ORDINALS 0x000a #define HUP_TELEPHONY 0x000b #define HUP_CONSUMER 0x000c #define HUP_DIGITIZERS 0x000d #define HUP_PHYSICAL_IFACE 0x000e #define HUP_UNICODE 0x0010 #define HUP_ALPHANUM_DISPLAY 0x0014 #define HUP_MONITOR 0x0080 #define HUP_MONITOR_ENUM_VAL 0x0081 #define HUP_VESA_VC 0x0082 #define HUP_VESA_CMD 0x0083 #define HUP_POWER 0x0084 #define HUP_BATTERY_SYSTEM 0x0085 #define HUP_BARCODE_SCANNER 0x008b #define HUP_SCALE 0x008c #define HUP_CAMERA_CONTROL 0x0090 #define HUP_ARCADE 0x0091 #define HUP_MICROSOFT 0xff00 /* Usages, generic desktop */ #define HUG_POINTER 0x0001 #define HUG_MOUSE 0x0002 #define HUG_JOYSTICK 0x0004 #define HUG_GAME_PAD 0x0005 #define HUG_KEYBOARD 0x0006 #define HUG_KEYPAD 0x0007 #define HUG_X 0x0030 #define HUG_Y 0x0031 #define HUG_Z 0x0032 #define HUG_RX 0x0033 #define HUG_RY 0x0034 #define HUG_RZ 0x0035 #define HUG_SLIDER 0x0036 #define HUG_DIAL 0x0037 #define HUG_WHEEL 0x0038 #define HUG_HAT_SWITCH 0x0039 #define HUG_COUNTED_BUFFER 0x003a #define HUG_BYTE_COUNT 0x003b #define HUG_MOTION_WAKEUP 0x003c #define HUG_VX 0x0040 #define HUG_VY 0x0041 #define HUG_VZ 0x0042 #define HUG_VBRX 0x0043 #define HUG_VBRY 0x0044 #define HUG_VBRZ 0x0045 #define HUG_VNO 0x0046 #define HUG_TWHEEL 0x0048 /* M$ Wireless Intellimouse Wheel */ #define HUG_SYSTEM_CONTROL 0x0080 #define HUG_SYSTEM_POWER_DOWN 0x0081 #define HUG_SYSTEM_SLEEP 0x0082 #define HUG_SYSTEM_WAKEUP 0x0083 #define HUG_SYSTEM_CONTEXT_MENU 0x0084 #define HUG_SYSTEM_MAIN_MENU 0x0085 #define HUG_SYSTEM_APP_MENU 0x0086 #define HUG_SYSTEM_MENU_HELP 0x0087 #define HUG_SYSTEM_MENU_EXIT 0x0088 #define HUG_SYSTEM_MENU_SELECT 0x0089 #define HUG_SYSTEM_MENU_RIGHT 0x008a #define HUG_SYSTEM_MENU_LEFT 0x008b #define HUG_SYSTEM_MENU_UP 0x008c #define HUG_SYSTEM_MENU_DOWN 0x008d /* Usages Digitizers */ #define HUD_UNDEFINED 0x0000 #define HUD_TIP_PRESSURE 0x0030 #define HUD_BARREL_PRESSURE 0x0031 #define HUD_IN_RANGE 0x0032 #define HUD_TOUCH 0x0033 #define HUD_UNTOUCH 0x0034 #define HUD_TAP 0x0035 #define HUD_QUALITY 0x0036 #define HUD_DATA_VALID 0x0037 #define HUD_TRANSDUCER_INDEX 0x0038 #define HUD_TABLET_FKEYS 0x0039 #define HUD_PROGRAM_CHANGE_KEYS 0x003a #define HUD_BATTERY_STRENGTH 0x003b #define HUD_INVERT 0x003c #define HUD_X_TILT 0x003d #define HUD_Y_TILT 0x003e #define HUD_AZIMUTH 0x003f #define HUD_ALTITUDE 0x0040 #define HUD_TWIST 0x0041 #define HUD_TIP_SWITCH 0x0042 #define HUD_SEC_TIP_SWITCH 0x0043 #define HUD_BARREL_SWITCH 0x0044 #define HUD_ERASER 0x0045 #define HUD_TABLET_PICK 0x0046 #define HID_USAGE2(p,u) (((p) << 16) | (u)) #define UHID_INPUT_REPORT 0x01 #define UHID_OUTPUT_REPORT 0x02 #define UHID_FEATURE_REPORT 0x03 /* Bits in the input/output/feature items */ #define HIO_CONST 0x001 #define HIO_VARIABLE 0x002 #define HIO_RELATIVE 0x004 #define HIO_WRAP 0x008 #define HIO_NONLINEAR 0x010 #define HIO_NOPREF 0x020 #define HIO_NULLSTATE 0x040 #define HIO_VOLATILE 0x080 #define HIO_BUFBYTES 0x100 #ifdef _KERNEL enum hid_kind { hid_input, hid_output, hid_feature, hid_collection, hid_endcollection }; struct hid_location { u_int32_t size; u_int32_t count; u_int32_t pos; }; struct hid_item { /* Global */ int32_t _usage_page; int32_t logical_minimum; int32_t logical_maximum; int32_t physical_minimum; int32_t physical_maximum; int32_t unit_exponent; int32_t unit; int32_t report_ID; /* Local */ int32_t usage; int32_t usage_minimum; int32_t usage_maximum; int32_t designator_index; int32_t designator_minimum; int32_t designator_maximum; int32_t string_index; int32_t string_minimum; int32_t string_maximum; int32_t set_delimiter; /* Misc */ int32_t collection; int collevel; enum hid_kind kind; u_int32_t flags; /* Location */ struct hid_location loc; /* */ struct hid_item *next; }; extern struct hid_data * hid_start_parse(const void *d, int len, int kindset); extern void hid_end_parse(struct hid_data *s); extern int hid_get_item(struct hid_data *s, struct hid_item *h); extern int hid_report_size(const void *buf, int len, enum hid_kind k, u_int8_t *id); extern int hid_locate(const void *desc, int size, u_int32_t usage, enum hid_kind kind, struct hid_location *loc, u_int32_t *flags); extern u_long hid_get_data(const u_char *buf, u_int32_t len, struct hid_location *loc); extern int hid_is_collection(const void *desc, int size, u_int32_t usage); #endif #endif /* _USBHID_H_ */ pwcbsd/usb/usb_if.m000644 000423 000000 00000003367 10551670754 015037 0ustar00luigiwheel000000 000000 #- # Copyright (c) 1992-1998 Nick Hibma # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer, # without modification, immediately at the beginning of the file. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # $FreeBSD: src/sys/dev/usb/usb_if.m,v 1.10 2005/01/06 01:43:29 imp Exp $ # # USB interface description # #include INTERFACE usb; # The device should start probing for new drivers again # METHOD int reconfigure { device_t dev; }; pwcbsd/usb/usb_port.h000644 000423 000000 00000017714 10551670754 015421 0ustar00luigiwheel000000 000000 /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifndef _USB_PORT_H #define _USB_PORT_H # ifdef _KERNEL # ifdef __FreeBSD__ # include # include /* bus_space_xxx() */ # include /* SYS_XXX */ # include /* device_xxx() */ # ifdef INCLUDE_PCIXXX_H /* NOTE: one does not want to include these * files when building the USB device driver * modules! */ # include # include # endif # include # include # include # include # include # include /* SYSCTL_XXX() */ # include # include # include /* callout_xxx() */ # include /* ETHER_XXX */ # ifndef __KASSERT typedef struct cdevsw cdevsw_t; # define __lockmgr lockmgr # define __KASSERT KASSERT # define uio_procp uio_td # endif # ifndef __callout_init_mtx # define __callout_init_mtx(c,m,f) callout_init_mtx(&(c)->co,m,f) # define __callout_reset(c,t,f,d) callout_reset(&(c)->co,t,f,d) # define __callout_stop(c) callout_stop(&(c)->co) # define __callout_drain(c) callout_drain(&(c)->co) # define __callout_pending(c) callout_pending(&(c)->co) struct __callout { struct callout co; }; # endif # else # include # endif # endif /* * Macros to cope with the differences between operating systems. */ #ifdef __NetBSD__ /* * NetBSD */ #include "opt_usbverbose.h" #define SCSI_MODE_SENSE MODE_SENSE #define usb_kthread_create1 kthread_create1 #define usb_kthread_create kthread_create #if (__NetBSD_Version__ >= 300000000) typedef void * usb_malloc_type; #else typedef int usb_malloc_type; #endif #define Ether_ifattach ether_ifattach #define IF_INPUT(ifp, m) (*(ifp)->if_input)((ifp), (m)) #elif defined(__OpenBSD__) /* * OpenBSD */ #define UCOMBUSCF_PORTNO -1 #define UCOMBUSCF_PORTNO_DEFAULT -1 #define SCSI_MODE_SENSE MODE_SENSE #define XS_STS_DONE ITSDONE #define XS_CTL_POLL SCSI_POLL #define XS_CTL_DATA_IN SCSI_DATA_IN #define XS_CTL_DATA_OUT SCSI_DATA_OUT #define scsipi_adapter scsi_adapter #define scsipi_cmd scsi_cmd #define scsipi_device scsi_device #define scsipi_done scsi_done #define scsipi_link scsi_link #define scsipi_minphys scsi_minphys #define scsipi_sense scsi_sense #define scsipi_xfer scsi_xfer #define xs_control flags #define xs_status status #define memcpy(d, s, l) bcopy((s),(d),(l)) #define memset(d, v, l) bzero((d),(l)) #define bswap32(x) swap32(x) #define bswap16(x) swap16(x) /* * The UHCI/OHCI controllers are little endian, so on big endian machines * the data strored in memory needs to be swapped. */ #if defined(letoh32) #define le32toh(x) letoh32(x) #define le16toh(x) letoh16(x) #endif #if (BYTE_ORDER == BIG_ENDIAN) #define htole32(x) (bswap32(x)) #define le32toh(x) (bswap32(x)) #else #define htole32(x) (x) #define le32toh(x) (x) #endif #define usb_kthread_create1 kthread_create #define usb_kthread_create kthread_create_deferred typedef int usb_malloc_type; #define Ether_ifattach(ifp, eaddr) ether_ifattach(ifp) #define if_deactivate(x) #define IF_INPUT(ifp, m) do { \ struct ether_header *eh; \ \ eh = mtod(m, struct ether_header *); \ m_adj(m, sizeof(struct ether_header)); \ ether_input((ifp), (eh), (m)); \ } while (0) #define powerhook_establish(fn, sc) (fn) #define powerhook_disestablish(hdl) #define PWR_RESUME 0 #define swap_bytes_change_sign16_le swap_bytes_change_sign16 #define change_sign16_swap_bytes_le change_sign16_swap_bytes #define change_sign16_le change_sign16 extern int cold; #elif defined(__FreeBSD__) /* * FreeBSD */ #include "opt_usb.h" #define usb_kthread_create1(f, s, p, ...) \ kthread_create((f), (s), (p), RFHIGHPID, 0, __VA_ARGS__) #define usb_kthread_create kthread_create #define clalloc(p, s, x) (clist_alloc_cblocks((p), (s), (s)), 0) #define clfree(p) clist_free_cblocks((p)) #define PWR_RESUME 0 #define PWR_SUSPEND 1 typedef struct malloc_type *usb_malloc_type; #endif /* __FreeBSD__ */ #define USBVERBOSE #ifndef Static #define Static static #endif #ifndef logprintf #define logprintf printf #endif #ifdef MALLOC_DECLARE MALLOC_DECLARE(M_USB); MALLOC_DECLARE(M_USBDEV); MALLOC_DECLARE(M_USBHC); #endif #ifdef SYSCTL_DECL SYSCTL_DECL(_hw_usb); #endif /* force debugging until further */ #ifndef USB_DEBUG #define USB_DEBUG #endif #ifdef USB_DEBUG #define PRINTF(x) { if (usbdebug) { printf("%s: ", __FUNCTION__); printf x ; } } #define PRINTFN(n,x) { if (usbdebug > (n)) { printf("%s: ", __FUNCTION__); printf x ; } } extern int usbdebug; #else #define PRINTF(x) #define PRINTFN(n,x) #endif #define USBD_CHECK_STATUS(xfer) \ { if((xfer)->flags & USBD_DEV_TRANSFERRING) \ { \ (xfer)->flags &= ~USBD_DEV_TRANSFERRING; \ if( (xfer)->error ) \ { goto tr_error; } \ else \ { goto tr_transferred; } \ } \ else \ { goto tr_setup; } \ } \ /**/ #define _MAKE_ENUM(enum,value,arg...) \ enum value, \ /**/ #define MAKE_ENUM(macro,end...) \ enum { macro(_MAKE_ENUM) end } \ /**/ #define __MAKE_TABLE(a...) a /* double pass to expand all macros */ #define _MAKE_TABLE(a...) (a), /* add comma */ #define MAKE_TABLE(m,field,p,a...) m##_##field p = { __MAKE_TABLE(m(m##_##field _MAKE_TABLE)) a } #ifndef LOG2 #define LOG2(x) ( \ ((x) <= (1<<0x0)) ? 0x0 : \ ((x) <= (1<<0x1)) ? 0x1 : \ ((x) <= (1<<0x2)) ? 0x2 : \ ((x) <= (1<<0x3)) ? 0x3 : \ ((x) <= (1<<0x4)) ? 0x4 : \ ((x) <= (1<<0x5)) ? 0x5 : \ ((x) <= (1<<0x6)) ? 0x6 : \ ((x) <= (1<<0x7)) ? 0x7 : \ ((x) <= (1<<0x8)) ? 0x8 : \ ((x) <= (1<<0x9)) ? 0x9 : \ ((x) <= (1<<0xA)) ? 0xA : \ ((x) <= (1<<0xB)) ? 0xB : \ ((x) <= (1<<0xC)) ? 0xC : \ ((x) <= (1<<0xD)) ? 0xD : \ ((x) <= (1<<0xE)) ? 0xE : \ ((x) <= (1<<0xF)) ? 0xF : \ 0x10) #endif /* LOG2 */ /* preliminary fix for a bug in msleep on FreeBSD, * which cannot sleep with Giant: */ #define msleep(i,m,p,w,t) msleep(i,(((m) == &Giant) ? NULL : (m)),p,w,t) #endif /* _USB_PORT_H */ pwcbsd/usb/usb_quirks.c000644 000423 000000 00000013527 10551670754 015744 0ustar00luigiwheel000000 000000 /* $NetBSD: usb_quirks.c,v 1.50 2004/06/23 02:30:52 mycroft Exp $ */ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD: src/sys/dev/usb/usb_quirks.c,v 1.50 2006/09/06 23:44:24 imp Exp $"); #include #include #include #include "usbdevs.h" #include #define ANY 0xffff static const struct usbd_quirk_entry { u_int16_t idVendor; u_int16_t idProduct; u_int16_t bcdDevice; struct usbd_quirks quirks; } usb_quirks[] = { { USB_VENDOR_KYE, USB_PRODUCT_KYE_NICHE, 0x100, { UQ_NO_SET_PROTO}}, { USB_VENDOR_INSIDEOUT, USB_PRODUCT_INSIDEOUT_EDGEPORT4, 0x094, { UQ_SWAP_UNICODE}}, { USB_VENDOR_DALLAS, USB_PRODUCT_DALLAS_J6502, 0x0a2, { UQ_BAD_ADC }}, { USB_VENDOR_DALLAS, USB_PRODUCT_DALLAS_J6502, 0x0a2, { UQ_AU_NO_XU }}, { USB_VENDOR_ALTEC, USB_PRODUCT_ALTEC_ADA70, 0x103, { UQ_BAD_ADC }}, { USB_VENDOR_ALTEC, USB_PRODUCT_ALTEC_ASC495, 0x000, { UQ_BAD_AUDIO }}, { USB_VENDOR_QTRONIX, USB_PRODUCT_QTRONIX_980N, 0x110, { UQ_SPUR_BUT_UP }}, { USB_VENDOR_ALCOR2, USB_PRODUCT_ALCOR2_KBD_HUB, 0x001, { UQ_SPUR_BUT_UP }}, { USB_VENDOR_MCT, USB_PRODUCT_MCT_HUB0100, 0x102, { UQ_BUS_POWERED }}, { USB_VENDOR_MCT, USB_PRODUCT_MCT_USB232, 0x102, { UQ_BUS_POWERED }}, { USB_VENDOR_METRICOM, USB_PRODUCT_METRICOM_RICOCHET_GS, 0x100, { UQ_ASSUME_CM_OVER_DATA }}, { USB_VENDOR_SANYO, USB_PRODUCT_SANYO_SCP4900, 0x000, { UQ_ASSUME_CM_OVER_DATA }}, { USB_VENDOR_TI, USB_PRODUCT_TI_UTUSB41, 0x110, { UQ_POWER_CLAIM }}, { USB_VENDOR_TELEX, USB_PRODUCT_TELEX_MIC1, 0x009, { UQ_AU_NO_FRAC }}, { USB_VENDOR_SILICONPORTALS, USB_PRODUCT_SILICONPORTALS_YAPPHONE, 0x100, { UQ_AU_INP_ASYNC }}, /* XXX These should have a revision number, but I don't know what they are. */ { USB_VENDOR_HP, USB_PRODUCT_HP_895C, ANY, { UQ_BROKEN_BIDIR }}, { USB_VENDOR_HP, USB_PRODUCT_HP_880C, ANY, { UQ_BROKEN_BIDIR }}, { USB_VENDOR_HP, USB_PRODUCT_HP_815C, ANY, { UQ_BROKEN_BIDIR }}, { USB_VENDOR_HP, USB_PRODUCT_HP_810C, ANY, { UQ_BROKEN_BIDIR }}, { USB_VENDOR_HP, USB_PRODUCT_HP_830C, ANY, { UQ_BROKEN_BIDIR }}, { USB_VENDOR_HP, USB_PRODUCT_HP_1220C, ANY, { UQ_BROKEN_BIDIR }}, /* YAMAHA router's ucdDevice is the version of farmware and often changes. */ { USB_VENDOR_YAMAHA, USB_PRODUCT_YAMAHA_RTA54I, ANY, { UQ_ASSUME_CM_OVER_DATA }}, { USB_VENDOR_YAMAHA, USB_PRODUCT_YAMAHA_RTA55I, ANY, { UQ_ASSUME_CM_OVER_DATA }}, { USB_VENDOR_YAMAHA, USB_PRODUCT_YAMAHA_RTW65B, ANY, { UQ_ASSUME_CM_OVER_DATA }}, { USB_VENDOR_YAMAHA, USB_PRODUCT_YAMAHA_RTW65I, ANY, { UQ_ASSUME_CM_OVER_DATA }}, { USB_VENDOR_QUALCOMM, USB_PRODUCT_QUALCOMM_CDMA_MSM, ANY, { UQ_ASSUME_CM_OVER_DATA }}, { USB_VENDOR_QUALCOMM2, USB_PRODUCT_QUALCOMM2_CDMA_MSM, ANY, { UQ_ASSUME_CM_OVER_DATA }}, { USB_VENDOR_SUNTAC, USB_PRODUCT_SUNTAC_AS64LX, 0x100, { UQ_ASSUME_CM_OVER_DATA }}, /* Devices which should be ignored by uhid */ { USB_VENDOR_APC, USB_PRODUCT_APC_UPS, ANY, { UQ_HID_IGNORE }}, { USB_VENDOR_DELORME, USB_PRODUCT_DELORME_EARTHMATE, ANY, { UQ_HID_IGNORE }}, { USB_VENDOR_MGE, USB_PRODUCT_MGE_UPS1, ANY, { UQ_HID_IGNORE }}, { USB_VENDOR_MGE, USB_PRODUCT_MGE_UPS2, ANY, { UQ_HID_IGNORE }}, { 0, 0, 0, { 0 } } }; const struct usbd_quirks usbd_no_quirk = { 0 }; const struct usbd_quirks * usbd_find_quirk(usb_device_descriptor_t *d) { const struct usbd_quirk_entry *t; u_int16_t vendor = UGETW(d->idVendor); u_int16_t product = UGETW(d->idProduct); u_int16_t revision = UGETW(d->bcdDevice); for (t = usb_quirks; t->idVendor != 0; t++) { if (t->idVendor == vendor && t->idProduct == product && (t->bcdDevice == ANY || t->bcdDevice == revision)) break; } #ifdef USB_DEBUG if (usbdebug && t->quirks.uq_flags) logprintf("usbd_find_quirk 0x%04x/0x%04x/%x: %d\n", UGETW(d->idVendor), UGETW(d->idProduct), UGETW(d->bcdDevice), t->quirks.uq_flags); #endif return (&t->quirks); } pwcbsd/usb/usb_quirks.h000644 000423 000000 00000006615 10551670754 015751 0ustar00luigiwheel000000 000000 /* $NetBSD: usb_quirks.h,v 1.20 2001/04/15 09:38:01 augustss Exp $ */ /* $FreeBSD: src/sys/dev/usb/usb_quirks.h,v 1.22 2006/02/19 14:48:02 iedowse Exp $ */ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ struct usbd_quirks { u_int32_t uq_flags; /* Device problems: */ #define UQ_NO_SET_PROTO 0x0001 /* cannot handle SET PROTOCOL. */ #define UQ_SWAP_UNICODE 0x0002 /* has some Unicode strings swapped. */ #define UQ_MS_REVZ 0x0004 /* mouse has Z-axis reversed */ #define UQ_NO_STRINGS 0x0008 /* string descriptors are broken. */ #define UQ_BAD_ADC 0x0010 /* bad audio spec version number. */ #define UQ_BUS_POWERED 0x0020 /* device is bus powered, despite claim */ #define UQ_BAD_AUDIO 0x0040 /* device claims audio class, but isn't */ #define UQ_SPUR_BUT_UP 0x0080 /* spurious mouse button up events */ #define UQ_AU_NO_XU 0x0100 /* audio device has broken extension unit */ #define UQ_POWER_CLAIM 0x0200 /* hub lies about power status */ #define UQ_AU_NO_FRAC 0x0400 /* don't adjust for fractional samples */ #define UQ_AU_INP_ASYNC 0x0800 /* input is async despite claim of adaptive */ #define UQ_ASSUME_CM_OVER_DATA 0x1000 /* modem device breaks on cm over data */ #define UQ_BROKEN_BIDIR 0x2000 /* printer has broken bidir mode */ #define UQ_OPEN_CLEARSTALL 0x4000 /* device needs clear endpoint stall */ #define UQ_HID_IGNORE 0x8000 /* device should be ignored by hid class */ }; extern const struct usbd_quirks usbd_no_quirk; const struct usbd_quirks *usbd_find_quirk(usb_device_descriptor_t *); pwcbsd/usb/usb_rdesc.h000644 000423 000000 00000035554 10551670754 015537 0ustar00luigiwheel000000 000000 /*- * Copyright (c) 2000 Nick Hibma * All rights reserved. * * Copyright (c) 2005 Ed Schouten * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD: src/sys/dev/usb/usb_rdesc.h,v 1.3 2005/12/31 04:38:50 mux Exp $ * * This file contains replacements for broken HID report descriptors. */ #define UHID_GRAPHIRE_REPORT_DESCR(...) \ 0x05, 0x0d, /* USAGE_PAGE (Digitizers) */\ 0x09, 0x01, /* USAGE (Digitizer) */\ 0xa1, 0x01, /* COLLECTION (Application) */\ 0x85, 0x02, /* REPORT_ID (2) */\ 0x05, 0x0d, /* USAGE_PAGE (Digitizers) */\ 0x09, 0x01, /* USAGE (Digitizer) */\ 0xa1, 0x00, /* COLLECTION (Physical) */\ 0x15, 0x00, /* LOGICAL_MINIMUM (0) */\ 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */\ 0x09, 0x33, /* USAGE (Touch) */\ 0x95, 0x01, /* REPORT_COUNT (1) */\ 0x75, 0x01, /* REPORT_SIZE (1) */\ 0x81, 0x02, /* INPUT (Data,Var,Abs) */\ 0x09, 0x44, /* USAGE (Barrel Switch) */\ 0x95, 0x02, /* REPORT_COUNT (2) */\ 0x75, 0x01, /* REPORT_SIZE (1) */\ 0x81, 0x02, /* INPUT (Data,Var,Abs) */\ 0x09, 0x00, /* USAGE (Undefined) */\ 0x95, 0x02, /* REPORT_COUNT (2) */\ 0x75, 0x01, /* REPORT_SIZE (1) */\ 0x81, 0x03, /* INPUT (Cnst,Var,Abs) */\ 0x09, 0x3c, /* USAGE (Invert) */\ 0x95, 0x01, /* REPORT_COUNT (1) */\ 0x75, 0x01, /* REPORT_SIZE (1) */\ 0x81, 0x02, /* INPUT (Data,Var,Abs) */\ 0x09, 0x38, /* USAGE (Transducer Index) */\ 0x95, 0x01, /* REPORT_COUNT (1) */\ 0x75, 0x01, /* REPORT_SIZE (1) */\ 0x81, 0x02, /* INPUT (Data,Var,Abs) */\ 0x09, 0x32, /* USAGE (In Range) */\ 0x95, 0x01, /* REPORT_COUNT (1) */\ 0x75, 0x01, /* REPORT_SIZE (1) */\ 0x81, 0x02, /* INPUT (Data,Var,Abs) */\ 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */\ 0x09, 0x30, /* USAGE (X) */\ 0x15, 0x00, /* LOGICAL_MINIMUM (0) */\ 0x26, 0xde, 0x27, /* LOGICAL_MAXIMUM (10206) */\ 0x95, 0x01, /* REPORT_COUNT (1) */\ 0x75, 0x10, /* REPORT_SIZE (16) */\ 0x81, 0x02, /* INPUT (Data,Var,Abs) */\ 0x09, 0x31, /* USAGE (Y) */\ 0x26, 0xfe, 0x1c, /* LOGICAL_MAXIMUM (7422) */\ 0x95, 0x01, /* REPORT_COUNT (1) */\ 0x75, 0x10, /* REPORT_SIZE (16) */\ 0x81, 0x02, /* INPUT (Data,Var,Abs) */\ 0x05, 0x0d, /* USAGE_PAGE (Digitizers) */\ 0x09, 0x30, /* USAGE (Tip Pressure) */\ 0x26, 0xff, 0x01, /* LOGICAL_MAXIMUM (511) */\ 0x95, 0x01, /* REPORT_COUNT (1) */\ 0x75, 0x10, /* REPORT_SIZE (16) */\ 0x81, 0x02, /* INPUT (Data,Var,Abs) */\ 0xc0, /* END_COLLECTION */\ 0x05, 0x0d, /* USAGE_PAGE (Digitizers) */\ 0x09, 0x00, /* USAGE (Undefined) */\ 0x85, 0x02, /* REPORT_ID (2) */\ 0x95, 0x01, /* REPORT_COUNT (1) */\ 0xb1, 0x02, /* FEATURE (Data,Var,Abs) */\ 0x09, 0x00, /* USAGE (Undefined) */\ 0x85, 0x03, /* REPORT_ID (3) */\ 0x95, 0x01, /* REPORT_COUNT (1) */\ 0xb1, 0x02, /* FEATURE (Data,Var,Abs) */\ 0xc0, /* END_COLLECTION */\ #define UHID_GRAPHIRE3_4X5_REPORT_DESCR(...) \ 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */\ 0x09, 0x02, /* USAGE (Mouse) */\ 0xa1, 0x01, /* COLLECTION (Application) */\ 0x85, 0x01, /* REPORT_ID (1) */\ 0x09, 0x01, /* USAGE (Pointer) */\ 0xa1, 0x00, /* COLLECTION (Physical) */\ 0x05, 0x09, /* USAGE_PAGE (Button) */\ 0x19, 0x01, /* USAGE_MINIMUM (Button 1) */\ 0x29, 0x03, /* USAGE_MAXIMUM (Button 3) */\ 0x15, 0x00, /* LOGICAL_MINIMUM (0) */\ 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */\ 0x95, 0x03, /* REPORT_COUNT (3) */\ 0x75, 0x01, /* REPORT_SIZE (1) */\ 0x81, 0x02, /* INPUT (Data,Var,Abs) */\ 0x95, 0x01, /* REPORT_COUNT (1) */\ 0x75, 0x05, /* REPORT_SIZE (5) */\ 0x81, 0x01, /* INPUT (Cnst,Ary,Abs) */\ 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */\ 0x09, 0x30, /* USAGE (X) */\ 0x09, 0x31, /* USAGE (Y) */\ 0x09, 0x38, /* USAGE (Wheel) */\ 0x15, 0x81, /* LOGICAL_MINIMUM (-127) */\ 0x25, 0x7f, /* LOGICAL_MAXIMUM (127) */\ 0x75, 0x08, /* REPORT_SIZE (8) */\ 0x95, 0x03, /* REPORT_COUNT (3) */\ 0x81, 0x06, /* INPUT (Data,Var,Rel) */\ 0xc0, /* END_COLLECTION */\ 0xc0, /* END_COLLECTION */\ 0x05, 0x0d, /* USAGE_PAGE (Digitizers) */\ 0x09, 0x01, /* USAGE (Pointer) */\ 0xa1, 0x01, /* COLLECTION (Applicaption) */\ 0x85, 0x02, /* REPORT_ID (2) */\ 0x05, 0x0d, /* USAGE_PAGE (Digitizers) */\ 0x09, 0x01, /* USAGE (Digitizer) */\ 0xa1, 0x00, /* COLLECTION (Physical) */\ 0x09, 0x33, /* USAGE (Touch) */\ 0x09, 0x44, /* USAGE (Barrel Switch) */\ 0x09, 0x44, /* USAGE (Barrel Switch) */\ 0x15, 0x00, /* LOGICAL_MINIMUM (0) */\ 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */\ 0x75, 0x01, /* REPORT_SIZE (1) */\ 0x95, 0x03, /* REPORT_COUNT (3) */\ 0x81, 0x02, /* INPUT (Data,Var,Abs) */\ 0x75, 0x01, /* REPORT_SIZE (1) */\ 0x95, 0x02, /* REPORT_COUNT (2) */\ 0x81, 0x01, /* INPUT (Cnst,Ary,Abs) */\ 0x09, 0x3c, /* USAGE (Invert) */\ 0x09, 0x38, /* USAGE (Transducer Index) */\ 0x09, 0x32, /* USAGE (In Range) */\ 0x75, 0x01, /* REPORT_SIZE (1) */\ 0x95, 0x03, /* REPORT_COUNT (3) */\ 0x81, 0x02, /* INPUT (Data,Var,Abs) */\ 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */\ 0x09, 0x30, /* USAGE (X) */\ 0x15, 0x00, /* LOGICAL_MINIMUM (0) */\ 0x26, 0xde, 0x27, /* LOGICAL_MAXIMUM (10206) */\ 0x75, 0x10, /* REPORT_SIZE (16) */\ 0x95, 0x01, /* REPORT_COUNT (1) */\ 0x81, 0x02, /* INPUT (Data,Var,Abs) */\ 0x09, 0x31, /* USAGE (Y) */\ 0x26, 0xfe, 0x1c, /* LOGICAL_MAXIMUM (7422) */\ 0x75, 0x10, /* REPORT_SIZE (16) */\ 0x95, 0x01, /* REPORT_COUNT (1) */\ 0x81, 0x02, /* INPUT (Data,Var,Abs) */\ 0x05, 0x0d, /* USAGE_PAGE (Digitizers) */\ 0x09, 0x30, /* USAGE (Tip Pressure) */\ 0x26, 0xff, 0x01, /* LOGICAL_MAXIMUM (511) */\ 0x75, 0x10, /* REPORT_SIZE (16) */\ 0x95, 0x01, /* REPORT_COUNT (1) */\ 0x81, 0x02, /* INPUT (Data,Var,Abs) */\ 0xc0, /* END_COLLECTION */\ 0x05, 0x0d, /* USAGE_PAGE (Digitizers) */\ 0x09, 0x00, /* USAGE (Undefined) */\ 0x85, 0x02, /* REPORT_ID (2) */\ 0x95, 0x01, /* REPORT_COUNT (1) */\ 0xb1, 0x02, /* FEATURE (Data,Var,Abs) */\ 0x09, 0x00, /* USAGE (Undefined) */\ 0x85, 0x03, /* REPORT_ID (3) */\ 0x95, 0x01, /* REPORT_COUNT (1) */\ 0xb1, 0x02, /* FEATURE (Data,Var,Abs) */\ 0xc0 /* END_COLLECTION */\ /* * The descriptor has no output report format, thus preventing you from * controlling the LEDs and the built-in rumblers. */ #define UHID_XB360GP_REPORT_DESCR(...) \ 0x05, 0x01, /* USAGE PAGE (Generic Desktop) */\ 0x09, 0x05, /* USAGE (Gamepad) */\ 0xa1, 0x01, /* COLLECTION (Application) */\ /* Unused */\ 0x75, 0x08, /* REPORT SIZE (8) */\ 0x95, 0x01, /* REPORT COUNT (1) */\ 0x81, 0x01, /* INPUT (Constant) */\ /* Byte count */\ 0x75, 0x08, /* REPORT SIZE (8) */\ 0x95, 0x01, /* REPORT COUNT (1) */\ 0x05, 0x01, /* USAGE PAGE (Generic Desktop) */\ 0x09, 0x3b, /* USAGE (Byte Count) */\ 0x81, 0x01, /* INPUT (Constant) */\ /* D-Pad */\ 0x05, 0x01, /* USAGE PAGE (Generic Desktop) */\ 0x09, 0x01, /* USAGE (Pointer) */\ 0xa1, 0x00, /* COLLECTION (Physical) */\ 0x75, 0x01, /* REPORT SIZE (1) */\ 0x15, 0x00, /* LOGICAL MINIMUM (0) */\ 0x25, 0x01, /* LOGICAL MAXIMUM (1) */\ 0x35, 0x00, /* PHYSICAL MINIMUM (0) */\ 0x45, 0x01, /* PHYSICAL MAXIMUM (1) */\ 0x95, 0x04, /* REPORT COUNT (4) */\ 0x05, 0x01, /* USAGE PAGE (Generic Desktop) */\ 0x09, 0x90, /* USAGE (D-Pad Up) */\ 0x09, 0x91, /* USAGE (D-Pad Down) */\ 0x09, 0x93, /* USAGE (D-Pad Left) */\ 0x09, 0x92, /* USAGE (D-Pad Right) */\ 0x81, 0x02, /* INPUT (Data, Variable, Absolute) */\ 0xc0, /* END COLLECTION */\ /* Buttons 5-11 */\ 0x75, 0x01, /* REPORT SIZE (1) */\ 0x15, 0x00, /* LOGICAL MINIMUM (0) */\ 0x25, 0x01, /* LOGICAL MAXIMUM (1) */\ 0x35, 0x00, /* PHYSICAL MINIMUM (0) */\ 0x45, 0x01, /* PHYSICAL MAXIMUM (1) */\ 0x95, 0x07, /* REPORT COUNT (7) */\ 0x05, 0x09, /* USAGE PAGE (Button) */\ 0x09, 0x08, /* USAGE (Button 8) */\ 0x09, 0x07, /* USAGE (Button 7) */\ 0x09, 0x09, /* USAGE (Button 9) */\ 0x09, 0x0a, /* USAGE (Button 10) */\ 0x09, 0x05, /* USAGE (Button 5) */\ 0x09, 0x06, /* USAGE (Button 6) */\ 0x09, 0x0b, /* USAGE (Button 11) */\ 0x81, 0x02, /* INPUT (Data, Variable, Absolute) */\ /* Unused */\ 0x75, 0x01, /* REPORT SIZE (1) */\ 0x95, 0x01, /* REPORT COUNT (1) */\ 0x81, 0x01, /* INPUT (Constant) */\ /* Buttons 1-4 */\ 0x75, 0x01, /* REPORT SIZE (1) */\ 0x15, 0x00, /* LOGICAL MINIMUM (0) */\ 0x25, 0x01, /* LOGICAL MAXIMUM (1) */\ 0x35, 0x00, /* PHYSICAL MINIMUM (0) */\ 0x45, 0x01, /* PHYSICAL MAXIMUM (1) */\ 0x95, 0x04, /* REPORT COUNT (4) */\ 0x05, 0x09, /* USAGE PAGE (Button) */\ 0x19, 0x01, /* USAGE MINIMUM (Button 1) */\ 0x29, 0x04, /* USAGE MAXIMUM (Button 4) */\ 0x81, 0x02, /* INPUT (Data, Variable, Absolute) */\ /* Triggers */\ 0x75, 0x08, /* REPORT SIZE (8) */\ 0x15, 0x00, /* LOGICAL MINIMUM (0) */\ 0x26, 0xff, 0x00, /* LOGICAL MAXIMUM (255) */\ 0x35, 0x00, /* PHYSICAL MINIMUM (0) */\ 0x46, 0xff, 0x00, /* PHYSICAL MAXIMUM (255) */\ 0x95, 0x02, /* REPORT SIZE (2) */\ 0x05, 0x01, /* USAGE PAGE (Generic Desktop) */\ 0x09, 0x32, /* USAGE (Z) */\ 0x09, 0x35, /* USAGE (Rz) */\ 0x81, 0x02, /* INPUT (Data, Variable, Absolute) */\ /* Sticks */\ 0x75, 0x10, /* REPORT SIZE (16) */\ 0x16, 0x00, 0x80, /* LOGICAL MINIMUM (-32768) */\ 0x26, 0xff, 0x7f, /* LOGICAL MAXIMUM (32767) */\ 0x36, 0x00, 0x80, /* PHYSICAL MINIMUM (-32768) */\ 0x46, 0xff, 0x7f, /* PHYSICAL MAXIMUM (32767) */\ 0x95, 0x04, /* REPORT COUNT (4) */\ 0x05, 0x01, /* USAGE PAGE (Generic Desktop) */\ 0x09, 0x30, /* USAGE (X) */\ 0x09, 0x31, /* USAGE (Y) */\ 0x09, 0x33, /* USAGE (Rx) */\ 0x09, 0x34, /* USAGE (Ry) */\ 0x81, 0x02, /* INPUT (Data, Variable, Absolute) */\ /* Unused */\ 0x75, 0x30, /* REPORT SIZE (48) */\ 0x95, 0x01, /* REPORT COUNT (1) */\ 0x81, 0x01, /* INPUT (Constant) */\ 0xc0 /* END COLLECTION */\ pwcbsd/usb/usb_requests.c000644 000423 000000 00000035763 10551670754 016307 0ustar00luigiwheel000000 000000 /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include __FBSDID("$FreeBSD: src/sys/dev/usb2/usbdi_util.c $"); usbd_status usbreq_reset_port(struct usbd_device *udev, int port, usb_port_status_t *ps) { usb_device_request_t req; usbd_status err; int n; req.bmRequestType = UT_WRITE_CLASS_OTHER; req.bRequest = UR_SET_FEATURE; USETW(req.wValue, UHF_PORT_RESET); USETW(req.wIndex, port); USETW(req.wLength, 0); err = usbd_do_request(udev, &req, 0); PRINTFN(1,("port %d reset done, error=%s\n", port, usbd_errstr(err))); if(err) { goto done; } n = 10; do { /* wait for device to recover from reset */ usbd_delay_ms(udev, USB_PORT_RESET_DELAY); err = usbreq_get_port_status(udev, port, ps); if(err) { PRINTF(("get status failed %d\n", err)); goto done; } /* if the device disappeared, just give up */ if(!(UGETW(ps->wPortStatus) & UPS_CURRENT_CONNECT_STATUS)) { err = USBD_NORMAL_COMPLETION; goto done; } } while (((UGETW(ps->wPortChange) & UPS_C_PORT_RESET) == 0) && (--n > 0)); if(n == 0) { err = USBD_TIMEOUT; goto done; } err = usbreq_clear_port_feature(udev, port, UHF_C_PORT_RESET); #ifdef USB_DEBUG if(err) { PRINTF(("clear port feature failed %d\n", err)); } #endif /* wait for the device to recover from reset */ usbd_delay_ms(udev, USB_PORT_RESET_RECOVERY); done: return (err); } usbd_status usbreq_get_desc(struct usbd_device *udev, int type, int index, int len, void *desc, int timeout) { usb_device_request_t req; usbd_status err; PRINTFN(3,("type=%d, index=%d, len=%d\n", type, index, len)); req.bmRequestType = UT_READ_DEVICE; req.bRequest = UR_GET_DESCRIPTOR; USETW2(req.wValue, type, index); USETW(req.wIndex, 0); USETW(req.wLength, len); repeat: err = usbd_do_request(udev, &req, desc); if(err && timeout--) { usbd_delay_ms(udev, 200); goto repeat; } return (err); } /* Use "usbreq_get_string_any()" instead of * "usbreq_get_string_desc()", when the language id is not known. The * maximum length of the string, "len", includes the terminating zero. * "usbreq_get_string_any()" will always write a terminating zero to "buf", * also on error. */ usbd_status usbreq_get_string_any(struct usbd_device *udev, int si, char *buf, int len) { int swap = udev->quirks->uq_flags & UQ_SWAP_UNICODE; usb_string_descriptor_t us; char *s; int i, n; u_int16_t c; usbd_status err; if(len == 0) { return (USBD_NORMAL_COMPLETION); } buf[0] = 0; /* subtract the terminating zero */ len--; if(si == 0) { return (USBD_INVAL); } if(udev->quirks->uq_flags & UQ_NO_STRINGS) { return (USBD_STALLED); } if(udev->langid == USBD_NOLANG) { /* set up default language */ err = usbreq_get_string_desc(udev, USB_LANGUAGE_TABLE, 0, &us, 0); if(err || (us.bLength < 4)) { udev->langid = 0; /* well, just pick something then */ } else { /* pick the first language as the default */ udev->langid = UGETW(us.bString[0]); } } err = usbreq_get_string_desc(udev, si, udev->langid, &us, 0); if(err) { return (err); } s = buf; n = (us.bLength / 2) - 1; for(i = 0; (i < n) && len; i++, len--) { c = UGETW(us.bString[i]); /* convert from Unicode, handle buggy strings */ if ((c & 0xff00) == 0) { *s++ = c; } else if(((c & 0x00ff) == 0) && swap) { *s++ = c >> 8; } else { *s++ = '?'; } } *s++ = 0; return (USBD_NORMAL_COMPLETION); } usbd_status usbreq_get_string_desc(struct usbd_device *udev, int sindex, int langid, usb_string_descriptor_t *sdesc, int *plen) { usb_device_request_t req; usbd_status err; int actlen; req.bmRequestType = UT_READ_DEVICE; req.bRequest = UR_GET_DESCRIPTOR; USETW2(req.wValue, UDESC_STRING, sindex); USETW(req.wIndex, langid); USETW(req.wLength, 2); /* only size byte first */ err = usbd_do_request_flags(udev, &req, sdesc, USBD_SHORT_XFER_OK, &actlen, USBD_DEFAULT_TIMEOUT); if(err) { return (err); } if(actlen < 2) { return (USBD_SHORT_XFER); } if(plen) { *plen = sdesc->bLength; } USETW(req.wLength, sdesc->bLength); /* the whole string */ return (usbd_do_request(udev, &req, sdesc)); } usbd_status usbreq_get_config_desc(struct usbd_device *udev, int confidx, usb_config_descriptor_t *d) { usbd_status err; PRINTFN(3,("confidx=%d\n", confidx)); err = usbreq_get_desc(udev, UDESC_CONFIG, confidx, USB_CONFIG_DESCRIPTOR_SIZE, d, 0); if(err) { return (err); } if(d->bDescriptorType != UDESC_CONFIG) { PRINTFN(-1,("confidx=%d, bad desc len=%d type=%d\n", confidx, d->bLength, d->bDescriptorType)); return (USBD_INVAL); } return (USBD_NORMAL_COMPLETION); } usbd_status usbreq_get_config_desc_full(struct usbd_device *udev, int conf, void *d, int size) { PRINTFN(3,("conf=%d\n", conf)); return (usbreq_get_desc(udev, UDESC_CONFIG, conf, size, d, 0)); } usbd_status usbreq_get_device_desc(struct usbd_device *udev, usb_device_descriptor_t *d) { PRINTFN(3,("\n")); return (usbreq_get_desc(udev, UDESC_DEVICE, 0, USB_DEVICE_DESCRIPTOR_SIZE, d, 3)); } usbd_status usbreq_get_interface(struct usbd_device *udev, u_int8_t iface_index, u_int8_t *aiface) { struct usbd_interface *iface = usbd_get_iface(udev,iface_index); usb_device_request_t req; if((iface == NULL) || (iface->idesc == NULL)) { return (USBD_INVAL); } req.bmRequestType = UT_READ_INTERFACE; req.bRequest = UR_GET_INTERFACE; USETW(req.wValue, 0); USETW(req.wIndex, iface->idesc->bInterfaceNumber); USETW(req.wLength, 1); return (usbd_do_request(udev, &req, aiface)); } usbd_status usbreq_set_interface(struct usbd_device *udev, u_int8_t iface_index, u_int8_t altno) { struct usbd_interface *iface = usbd_get_iface(udev,iface_index); usb_device_request_t req; usbd_status err; if(iface == NULL) { return (USBD_INVAL); } err = usbd_fill_iface_data(udev, iface_index, altno); if(err) { return (err); } req.bmRequestType = UT_WRITE_INTERFACE; req.bRequest = UR_SET_INTERFACE; USETW(req.wValue, iface->idesc->bAlternateSetting); USETW(req.wIndex, iface->idesc->bInterfaceNumber); USETW(req.wLength, 0); return (usbd_do_request(udev, &req, 0)); } usbd_status usbreq_get_device_status(struct usbd_device *udev, usb_status_t *st) { usb_device_request_t req; req.bmRequestType = UT_READ_DEVICE; req.bRequest = UR_GET_STATUS; USETW(req.wValue, 0); USETW(req.wIndex, 0); USETW(req.wLength, sizeof(usb_status_t)); return (usbd_do_request(udev, &req, st)); } usbd_status usbreq_get_hub_descriptor(struct usbd_device *udev, usb_hub_descriptor_t *hd) { usb_device_request_t req; req.bmRequestType = UT_READ_CLASS_DEVICE; req.bRequest = UR_GET_DESCRIPTOR; USETW(req.wValue, 0); USETW(req.wIndex, 0); USETW(req.wLength, USB_HUB_DESCRIPTOR_SIZE); return (usbd_do_request(udev, &req, hd)); } usbd_status usbreq_get_hub_status(struct usbd_device *udev, usb_hub_status_t *st) { usb_device_request_t req; req.bmRequestType = UT_READ_CLASS_DEVICE; req.bRequest = UR_GET_STATUS; USETW(req.wValue, 0); USETW(req.wIndex, 0); USETW(req.wLength, sizeof(usb_hub_status_t)); return (usbd_do_request(udev, &req, st)); } usbd_status usbreq_set_address(struct usbd_device *udev, int addr) { usb_device_request_t req; PRINTFN(5,("setting device address=%d\n", addr)); req.bmRequestType = UT_WRITE_DEVICE; req.bRequest = UR_SET_ADDRESS; USETW(req.wValue, addr); USETW(req.wIndex, 0); USETW(req.wLength, 0); return (usbd_do_request(udev, &req, 0)); } usbd_status usbreq_get_port_status(struct usbd_device *udev, int port, usb_port_status_t *ps) { usb_device_request_t req; req.bmRequestType = UT_READ_CLASS_OTHER; req.bRequest = UR_GET_STATUS; USETW(req.wValue, 0); USETW(req.wIndex, port); USETW(req.wLength, sizeof *ps); return (usbd_do_request(udev, &req, ps)); } usbd_status usbreq_clear_hub_feature(struct usbd_device *udev, int sel) { usb_device_request_t req; req.bmRequestType = UT_WRITE_CLASS_DEVICE; req.bRequest = UR_CLEAR_FEATURE; USETW(req.wValue, sel); USETW(req.wIndex, 0); USETW(req.wLength, 0); return (usbd_do_request(udev, &req, 0)); } usbd_status usbreq_set_hub_feature(struct usbd_device *udev, int sel) { usb_device_request_t req; req.bmRequestType = UT_WRITE_CLASS_DEVICE; req.bRequest = UR_SET_FEATURE; USETW(req.wValue, sel); USETW(req.wIndex, 0); USETW(req.wLength, 0); return (usbd_do_request(udev, &req, 0)); } usbd_status usbreq_clear_port_feature(struct usbd_device *udev, int port, int sel) { usb_device_request_t req; req.bmRequestType = UT_WRITE_CLASS_OTHER; req.bRequest = UR_CLEAR_FEATURE; USETW(req.wValue, sel); USETW(req.wIndex, port); USETW(req.wLength, 0); return (usbd_do_request(udev, &req, 0)); } usbd_status usbreq_set_port_feature(struct usbd_device *udev, int port, int sel) { usb_device_request_t req; req.bmRequestType = UT_WRITE_CLASS_OTHER; req.bRequest = UR_SET_FEATURE; USETW(req.wValue, sel); USETW(req.wIndex, port); USETW(req.wLength, 0); return (usbd_do_request(udev, &req, 0)); } usbd_status usbreq_set_protocol(struct usbd_device *udev, u_int8_t iface_index, u_int16_t report) { struct usbd_interface *iface = usbd_get_iface(udev,iface_index); usb_device_request_t req; if((iface == NULL) || (iface->idesc == NULL)) { return (USBD_INVAL); } PRINTFN(4, ("iface=%p, report=%d, endpt=%d\n", iface, report, iface->idesc->bInterfaceNumber)); req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UR_SET_PROTOCOL; USETW(req.wValue, report); USETW(req.wIndex, iface->idesc->bInterfaceNumber); USETW(req.wLength, 0); return (usbd_do_request(udev, &req, 0)); } usbd_status usbreq_set_report(struct usbd_device *udev, u_int8_t iface_index, u_int8_t type, u_int8_t id, void *data, int len) { struct usbd_interface *iface = usbd_get_iface(udev,iface_index); usb_device_request_t req; if((iface == NULL) || (iface->idesc == NULL)) { return (USBD_INVAL); } PRINTFN(4, ("len=%d\n", len)); req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UR_SET_REPORT; USETW2(req.wValue, type, id); USETW(req.wIndex, iface->idesc->bInterfaceNumber); USETW(req.wLength, len); return (usbd_do_request(udev, &req, data)); } usbd_status usbreq_get_report(struct usbd_device *udev, u_int8_t iface_index, u_int8_t type, u_int8_t id, void *data, int len) { struct usbd_interface *iface = usbd_get_iface(udev,iface_index); usb_device_request_t req; if((iface == NULL) || (iface->idesc == NULL) || (id == 0)) { return (USBD_INVAL); } PRINTFN(4, ("len=%d\n", len)); req.bmRequestType = UT_READ_CLASS_INTERFACE; req.bRequest = UR_GET_REPORT; USETW2(req.wValue, type, id); USETW(req.wIndex, iface->idesc->bInterfaceNumber); USETW(req.wLength, len); return (usbd_do_request(udev, &req, data)); } usbd_status usbreq_set_idle(struct usbd_device *udev, u_int8_t iface_index, int duration, int id) { struct usbd_interface *iface = usbd_get_iface(udev,iface_index); usb_device_request_t req; if((iface == NULL) || (iface->idesc == NULL)) { return (USBD_INVAL); } PRINTFN(4, ("%d %d\n", duration, id)); req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UR_SET_IDLE; USETW2(req.wValue, duration, id); USETW(req.wIndex, iface->idesc->bInterfaceNumber); USETW(req.wLength, 0); return (usbd_do_request(udev, &req, 0)); } usbd_status usbreq_get_report_descriptor(struct usbd_device *udev, int ifcno, int size, void *d) { usb_device_request_t req; req.bmRequestType = UT_READ_INTERFACE; req.bRequest = UR_GET_DESCRIPTOR; USETW2(req.wValue, UDESC_REPORT, 0); /* report id should be 0 */ USETW(req.wIndex, ifcno); USETW(req.wLength, size); return (usbd_do_request(udev, &req, d)); } usbd_status usbreq_read_report_desc(struct usbd_device *udev, u_int8_t iface_index, void **descp, int *sizep, usb_malloc_type mem) { struct usbd_interface *iface = usbd_get_iface(udev,iface_index); usb_hid_descriptor_t *hid; usbd_status err; if((iface == NULL) || (iface->idesc == NULL)) { return (USBD_INVAL); } hid = usbd_get_hdesc(usbd_get_config_descriptor(udev), iface->idesc); if(hid == NULL) { return (USBD_IOERROR); } *sizep = UGETW(hid->descrs[0].wDescriptorLength); *descp = malloc(*sizep, mem, M_NOWAIT); if(*descp == NULL) { return (USBD_NOMEM); } err = usbreq_get_report_descriptor(udev, iface->idesc->bInterfaceNumber, *sizep, *descp); if(err) { free(*descp, mem); *descp = NULL; return (err); } return (USBD_NORMAL_COMPLETION); } usbd_status usbreq_set_config(struct usbd_device *udev, int conf) { usb_device_request_t req; PRINTF(("setting config %d\n", conf)); req.bmRequestType = UT_WRITE_DEVICE; req.bRequest = UR_SET_CONFIG; USETW(req.wValue, conf); USETW(req.wIndex, 0); USETW(req.wLength, 0); return (usbd_do_request(udev, &req, 0)); } usbd_status usbreq_get_config(struct usbd_device *udev, u_int8_t *conf) { usb_device_request_t req; req.bmRequestType = UT_READ_DEVICE; req.bRequest = UR_GET_CONFIG; USETW(req.wValue, 0); USETW(req.wIndex, 0); USETW(req.wLength, 1); return (usbd_do_request(udev, &req, conf)); } pwcbsd/usb/usb_subr.c000644 000423 000000 00000170776 10551670754 015413 0ustar00luigiwheel000000 000000 /* $NetBSD: usb_subr.c,v 1.99 2002/07/11 21:14:34 augustss Exp $ */ /* Also already have from NetBSD: * $NetBSD: usb_subr.c,v 1.102 2003/01/01 16:21:50 augustss Exp $ * $NetBSD: usb_subr.c,v 1.103 2003/01/10 11:19:13 augustss Exp $ * $NetBSD: usb_subr.c,v 1.111 2004/03/15 10:35:04 augustss Exp $ * $NetBSD: usb_subr.c,v 1.114 2004/06/23 02:30:52 mycroft Exp $ * $NetBSD: usb_subr.c,v 1.115 2004/06/23 05:23:19 mycroft Exp $ * $NetBSD: usb_subr.c,v 1.116 2004/06/23 06:27:54 mycroft Exp $ * $NetBSD: usb_subr.c,v 1.119 2004/10/23 13:26:33 augustss Exp $ */ #include __FBSDID("$FreeBSD: src/sys/dev/usb/usb_subr.c,v 1.86 2006/09/10 15:20:39 trhodes Exp $"); /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include /* LIST_XXX() */ #include #include #include #include #include #include #include #include #include #include "usbdevs.h" #include #ifdef USBVERBOSE /* * Descriptions of of known vendors and devices ("products"). */ struct usb_knowndev { u_int16_t vendor; u_int16_t product; int flags; char *vendorname, *productname; }; #define USB_KNOWNDEV_NOPROD 0x01 /* match on vendor only */ #include "usbdevs_data.h" #endif /* USBVERBOSE */ static void usbd_trim_spaces(char *p) { char *q, *e; if(p == NULL) return; q = e = p; while (*q == ' ') /* skip leading spaces */ q++; while ((*p = *q++)) /* copy string */ if (*p++ != ' ') /* remember last non-space */ e = p; *e = 0; /* kill trailing spaces */ return; } static void usbd_devinfo_vp(struct usbd_device *udev, char *v, char *p, int usedev) { usb_device_descriptor_t *udd = &udev->ddesc; char *vendor, *product; #ifdef USBVERBOSE const struct usb_knowndev *kdp; #endif if(udev == NULL) { v[0] = p[0] = '\0'; return; } vendor = NULL; product = NULL; if (usedev) { (void) usbreq_get_string_any (udev, udd->iManufacturer, v, USB_MAX_STRING_LEN); vendor = v; usbd_trim_spaces(vendor); if(!vendor[0]) { vendor = NULL; } (void) usbreq_get_string_any (udev, udd->iProduct, p, USB_MAX_STRING_LEN); product = p; usbd_trim_spaces(product); if(!product[0]) { product = NULL; } } #ifdef USBVERBOSE if (vendor == NULL || product == NULL) { for(kdp = usb_knowndevs; kdp->vendorname != NULL; kdp++) { if (kdp->vendor == UGETW(udd->idVendor) && (kdp->product == UGETW(udd->idProduct) || (kdp->flags & USB_KNOWNDEV_NOPROD) != 0)) break; } if (kdp->vendorname != NULL) { if (vendor == NULL) vendor = kdp->vendorname; if (product == NULL) product = (kdp->flags & USB_KNOWNDEV_NOPROD) == 0 ? kdp->productname : NULL; } } #endif if (vendor != NULL && *vendor) strcpy(v, vendor); else sprintf(v, "vendor 0x%04x", UGETW(udd->idVendor)); if (product != NULL && *product) strcpy(p, product); else sprintf(p, "product 0x%04x", UGETW(udd->idProduct)); return; } static int usbd_printBCD(char *cp, int bcd) { return (sprintf(cp, "%x.%02x", bcd >> 8, bcd & 0xff)); } void usbd_devinfo(struct usbd_device *udev, int showclass, char *dst_ptr, u_int16_t dst_len) { usb_device_descriptor_t *udd = &udev->ddesc; char vendor[USB_MAX_STRING_LEN]; char product[USB_MAX_STRING_LEN]; u_int16_t bcdDevice, bcdUSB; usbd_devinfo_vp(udev, vendor, product, 1); bcdUSB = UGETW(udd->bcdUSB); bcdDevice = UGETW(udd->bcdDevice); if(showclass) { snprintf(dst_ptr, dst_len, "%s %s, class %d/%d, rev %x.%02x/" "%x.%02x, addr %d", vendor, product, udd->bDeviceClass, udd->bDeviceSubClass, (bcdUSB >> 8), bcdUSB & 0xFF, (bcdDevice >> 8), bcdDevice & 0xFF, udev->address); } else { snprintf(dst_ptr, dst_len, "%s %s, rev %x.%02x/" "%x.%02x, addr %d", vendor, product, (bcdUSB >> 8), bcdUSB & 0xFF, (bcdDevice >> 8), bcdDevice & 0xFF, udev->address); } return; } const char * usbd_errstr(usbd_status err) { static const char * const MAKE_TABLE(USBD_STATUS,DESC,[]); return (err < N_USBD_STATUS) ? USBD_STATUS_DESC[err] : "unknown error!"; } /* Delay for a certain number of ms */ void usb_delay_ms(struct usbd_bus *bus, u_int ms) { /* Wait at least two clock ticks so we know the time has passed. */ if (bus->use_polling || cold) DELAY((ms+1) * 1000); else tsleep(&ms, PRIBIO, "usbdly", (((ms*hz)+999)/1000) + 1); } /* Delay given a device handle. */ void usbd_delay_ms(struct usbd_device *udev, u_int ms) { usb_delay_ms(udev->bus, ms); } #define ADD_BYTES(ptr,len) ((void *)(((u_int8_t *)(ptr)) + (len))) usb_descriptor_t * usbd_desc_foreach(usb_config_descriptor_t *cd, usb_descriptor_t *desc) { void *end; if (cd == NULL) { return NULL; } end = ADD_BYTES(cd, UGETW(cd->wTotalLength)); if (desc == NULL) { desc = ADD_BYTES(cd, 0); } else { desc = ADD_BYTES(desc, desc->bLength); } return (((((void *)desc) >= ((void *)cd)) && (((void *)desc) < end) && (ADD_BYTES(desc,desc->bLength) >= ((void *)cd)) && (ADD_BYTES(desc,desc->bLength) <= end) && (desc->bLength >= sizeof(*desc))) ? desc : NULL); } usb_hid_descriptor_t * usbd_get_hdesc(usb_config_descriptor_t *cd, usb_interface_descriptor_t *id) { usb_descriptor_t *desc = (void *)id; if(desc == NULL) { return NULL; } while ((desc = usbd_desc_foreach(cd, desc))) { if ((desc->bDescriptorType == UDESC_HID) && (desc->bLength >= USB_HID_DESCRIPTOR_SIZE(0))) { return (void *)desc; } if (desc->bDescriptorType == UDESC_INTERFACE) { break; } } return NULL; } usb_interface_descriptor_t * usbd_find_idesc(usb_config_descriptor_t *cd, u_int16_t iface_index, u_int16_t alt_index) { usb_descriptor_t *desc = NULL; usb_interface_descriptor_t *id; u_int16_t curidx = 0xFFFF; u_int16_t lastidx = 0xFFFF; u_int16_t curaidx = 0; while ((desc = usbd_desc_foreach(cd, desc))) { if ((desc->bDescriptorType == UDESC_INTERFACE) && (desc->bLength >= sizeof(*id))) { id = (void *)desc; if(id->bInterfaceNumber != lastidx) { lastidx = id->bInterfaceNumber; curidx++; curaidx = 0; } else { curaidx++; } if((iface_index == curidx) && (alt_index == curaidx)) { return (id); } } } return (NULL); } usb_endpoint_descriptor_t * usbd_find_edesc(usb_config_descriptor_t *cd, u_int16_t iface_index, u_int16_t alt_index, u_int16_t endptidx) { usb_descriptor_t *desc = NULL; usb_interface_descriptor_t *d; u_int16_t curidx = 0; d = usbd_find_idesc(cd, iface_index, alt_index); if (d == NULL) return NULL; if (endptidx >= d->bNumEndpoints) /* quick exit */ return NULL; desc = ((void *)d); while ((desc = usbd_desc_foreach(cd, desc))) { if(desc->bDescriptorType == UDESC_INTERFACE) { break; } if (desc->bDescriptorType == UDESC_ENDPOINT) { if (curidx == endptidx) { return ((desc->bLength >= USB_ENDPOINT_DESCRIPTOR_SIZE) ? ((void *)d) : NULL); } curidx++; } } return (NULL); } usb_descriptor_t * usbd_find_descriptor(usb_config_descriptor_t *cd, int type, int subtype) { usb_descriptor_t *desc = NULL; while ((desc = usbd_desc_foreach(cd, desc))) { if((desc->bDescriptorType == type) && ((subtype == USBD_SUBTYPE_ANY) || (subtype == desc->bDescriptorSubtype))) { return desc; } } return (NULL); } int usbd_get_no_alts(usb_config_descriptor_t *cd, u_int8_t ifaceno) { usb_descriptor_t *desc = NULL; usb_interface_descriptor_t *id; int n = 0; while ((desc = usbd_desc_foreach(cd, desc))) { if ((desc->bDescriptorType == UDESC_INTERFACE) && (desc->bLength >= sizeof(*id))) { id = (void *)desc; if (id->bInterfaceNumber == ifaceno) { n++; } } } return n; } static void usbd_fill_pipe_data(struct usbd_device *udev, u_int8_t iface_index, usb_endpoint_descriptor_t *edesc, struct usbd_pipe *pipe) { bzero(pipe, sizeof(*pipe)); pipe->edesc = edesc; pipe->iface_index = iface_index; LIST_INIT(&pipe->list_head); /* first transfer needs to clear stall! */ pipe->clearstall = 1; (udev->bus->methods->pipe_init)(udev,edesc,pipe); return; } /* NOTE: pipes should not be in use when * ``usbd_free_pipe_data()'' is called */ static void usbd_free_pipe_data(struct usbd_device *udev, int iface_index) { struct usbd_pipe *pipe = &udev->pipes[0]; struct usbd_pipe *pipe_end = &udev->pipes_end[0]; while(pipe < pipe_end) { if((iface_index == pipe->iface_index) || (iface_index == -1)) { /* free pipe */ pipe->edesc = NULL; } pipe++; } return; } usbd_status usbd_fill_iface_data(struct usbd_device *udev, int iface_index, int alt_index) { struct usbd_interface *iface = usbd_get_iface(udev,iface_index); struct usbd_pipe *pipe = &udev->pipes[0]; struct usbd_pipe *pipe_end = &udev->pipes_end[0]; usb_interface_descriptor_t *id; usb_endpoint_descriptor_t *ed = NULL; usb_descriptor_t *desc; u_int8_t nendpt; if(iface == NULL) { return (USBD_INVAL); } PRINTFN(4,("iface_index=%d alt_index=%d\n", iface_index, alt_index)); /* mtx_assert() */ while(pipe < pipe_end) { if(pipe->iface_index == iface_index) { if(pipe->refcount) { return(USBD_IN_USE); } } pipe++; } pipe = &udev->pipes[0]; /* free old pipes if any */ usbd_free_pipe_data(udev, iface_index); id = usbd_find_idesc(udev->cdesc, iface_index, alt_index); if(id == NULL) { return (USBD_INVAL); } iface->idesc = id; iface->alt_index = alt_index; USBD_CLR_IFACE_NO_PROBE(udev, iface_index); nendpt = id->bNumEndpoints; PRINTFN(4,("found idesc nendpt=%d\n", nendpt)); desc = (void *)id; while(nendpt--) { PRINTFN(10,("endpt=%d\n", nendpt)); while ((desc = usbd_desc_foreach(udev->cdesc, desc))) { if ((desc->bDescriptorType == UDESC_ENDPOINT) && (desc->bLength >= USB_ENDPOINT_DESCRIPTOR_SIZE)) { goto found; } if (desc->bDescriptorType == UDESC_INTERFACE) { break; } } goto error; found: ed = (void *)desc; if(udev->speed == USB_SPEED_HIGH) { u_int16_t mps; /* control and bulk endpoints have max packet limits */ switch (UE_GET_XFERTYPE(ed->bmAttributes)) { case UE_CONTROL: mps = USB_2_MAX_CTRL_PACKET; goto check; case UE_BULK: mps = USB_2_MAX_BULK_PACKET; check: if(UGETW(ed->wMaxPacketSize) != mps) { USETW(ed->wMaxPacketSize, mps); #ifdef DIAGNOSTIC printf("%s: bad wMaxPacketSize, addr=%d!\n", __FUNCTION__, udev->address); #endif } break; default: break; } } if(UGETW(ed->wMaxPacketSize) == 0) { #ifdef USB_DEBUG printf("%s: invalid wMaxPacketSize, addr=%d!\n", __FUNCTION__, udev->address); #endif /* avoid division by zero * (in EHCI/UHCI/OHCI drivers) */ USETW(ed->wMaxPacketSize, USB_MAX_IPACKET); } /* find a free pipe */ while(pipe < pipe_end) { if(pipe->edesc == NULL) { /* pipe is free */ usbd_fill_pipe_data(udev,iface_index,ed,pipe); break; } pipe++; } } return (USBD_NORMAL_COMPLETION); error: /* passed end, or bad desc */ printf("%s: bad descriptor(s), addr=%d!\n", __FUNCTION__, udev->address); /* free old pipes if any */ usbd_free_pipe_data(udev, iface_index); return (USBD_INVAL); } static void usbd_free_iface_data(struct usbd_device *udev) { struct usbd_interface *iface = &udev->ifaces[0]; struct usbd_interface *iface_end = &udev->ifaces_end[0]; /* mtx_assert() */ /* free all pipes, if any */ usbd_free_pipe_data(udev, -1); /* free all interfaces, if any */ while(iface < iface_end) { iface->idesc = NULL; iface++; } if(udev->cdesc != NULL) { /* free "cdesc" after "ifaces" */ free(udev->cdesc, M_USB); } udev->cdesc = NULL; udev->config = USB_UNCONFIG_NO; return; } /* - USB config 0 * - USB interfaces * - USB alternative interfaces * - USB pipes * * - USB config 1 * - USB interfaces * - USB alternative interfaces * - USB pipes */ usbd_status usbd_search_and_set_config(struct usbd_device *udev, int no, int msg) { usb_config_descriptor_t cd; usbd_status err; int index; if(no == USB_UNCONFIG_NO) { return (usbd_set_config_index(udev, USB_UNCONFIG_INDEX, msg)); } PRINTFN(5,("%d\n", no)); /* figure out what config index to use */ for(index = 0; index < udev->ddesc.bNumConfigurations; index++) { err = usbreq_get_config_desc(udev, index, &cd); if(err) { return (err); } if(cd.bConfigurationValue == no) { return (usbd_set_config_index(udev, index, msg)); } } return (USBD_INVAL); } usbd_status usbd_set_config_index(struct usbd_device *udev, int index, int msg) { usb_status_t ds; usb_hub_descriptor_t hd; usb_config_descriptor_t cd, *cdp; usbd_status err; int nifc, len, selfpowered, power; PRINTFN(5,("udev=%p index=%d\n", udev, index)); if(index == USB_UNCONFIG_INDEX) { /* leave unallocated when * unconfiguring the device */ err = usbreq_set_config(udev, USB_UNCONFIG_NO); goto error; } /* get the short descriptor */ err = usbreq_get_config_desc(udev, index, &cd); if(err) { goto error; } /* free all configuration data structures */ usbd_free_iface_data(udev); /* get full descriptor */ len = UGETW(cd.wTotalLength); udev->cdesc = malloc(len, M_USB, M_NOWAIT|M_ZERO); if(udev->cdesc == NULL) { return (USBD_NOMEM); } cdp = udev->cdesc; /* get the full descriptor */ err = usbreq_get_desc(udev, UDESC_CONFIG, index, len, cdp, 3); if (err) goto error; if(cdp->bDescriptorType != UDESC_CONFIG) { PRINTF(("bad desc %d\n", cdp->bDescriptorType)); err = USBD_INVAL; goto error; } if(cdp->bNumInterface > (sizeof(udev->ifaces)/sizeof(udev->ifaces[0]))) { PRINTF(("too many interfaces: %d\n", cdp->bNumInterface)); cdp->bNumInterface = (sizeof(udev->ifaces)/sizeof(udev->ifaces[0])); } /* Figure out if the device is self or bus powered. */ selfpowered = 0; if (!(udev->quirks->uq_flags & UQ_BUS_POWERED) && (cdp->bmAttributes & UC_SELF_POWERED)) { /* May be self powered. */ if (cdp->bmAttributes & UC_BUS_POWERED) { /* Must ask device. */ if (udev->quirks->uq_flags & UQ_POWER_CLAIM) { /* * Hub claims to be self powered, but isn't. * It seems that the power status can be * determined by the hub characteristics. */ err = usbreq_get_hub_descriptor(udev, &hd); if(!err && (UGETW(hd.wHubCharacteristics) & UHD_PWR_INDIVIDUAL)) { selfpowered = 1; } PRINTF(("characteristics=0x%04x, error=%s\n", UGETW(hd.wHubCharacteristics), usbd_errstr(err))); } else { err = usbreq_get_device_status(udev, &ds); if(!err && (UGETW(ds.wStatus) & UDS_SELF_POWERED)) { selfpowered = 1; } PRINTF(("status=0x%04x, error=%s\n", UGETW(ds.wStatus), usbd_errstr(err))); } } else selfpowered = 1; } PRINTF(("udev=%p cdesc=%p (addr %d) cno=%d attr=0x%02x, " "selfpowered=%d, power=%d\n", udev, cdp, cdp->bConfigurationValue, udev->address, cdp->bmAttributes, selfpowered, cdp->bMaxPower * 2)); /* Check if we have enough power. */ power = cdp->bMaxPower * 2; if(power > udev->powersrc->power) { PRINTF(("power exceeded %d %d\n", power, udev->powersrc->power)); /* XXX print nicer message */ if(msg) { device_printf(udev->bus->bdev, "device addr %d (config %d) exceeds power " "budget, %d mA > %d mA\n", udev->address, cdp->bConfigurationValue, power, udev->powersrc->power); } err = USBD_NO_POWER; goto error; } udev->power = power; udev->self_powered = selfpowered; udev->config = cdp->bConfigurationValue; /* Set the actual configuration value. */ err = usbreq_set_config(udev, cdp->bConfigurationValue); if(err) { goto error; } /* Allocate and fill interface data. */ nifc = cdp->bNumInterface; while(nifc--) { err = usbd_fill_iface_data(udev, nifc, 0); if (err) { goto error; } } return (USBD_NORMAL_COMPLETION); error: PRINTF(("error=%s\n", usbd_errstr(err))); usbd_free_iface_data(udev); return (err); } int usbd_fill_deviceinfo(struct usbd_device *udev, struct usb_device_info *di, int usedev) { struct usbd_port *p; int i, err, s; if((udev == NULL) || (di == NULL)) { return (ENXIO); } bzero(di, sizeof(di[0])); di->udi_bus = device_get_unit(udev->bus->bdev); di->udi_addr = udev->address; di->udi_cookie = udev->cookie; usbd_devinfo_vp(udev, di->udi_vendor, di->udi_product, usedev); usbd_printBCD(di->udi_release, UGETW(udev->ddesc.bcdDevice)); di->udi_vendorNo = UGETW(udev->ddesc.idVendor); di->udi_productNo = UGETW(udev->ddesc.idProduct); di->udi_releaseNo = UGETW(udev->ddesc.bcdDevice); di->udi_class = udev->ddesc.bDeviceClass; di->udi_subclass = udev->ddesc.bDeviceSubClass; di->udi_protocol = udev->ddesc.bDeviceProtocol; di->udi_config = udev->config; di->udi_power = udev->self_powered ? 0 : udev->power; di->udi_speed = udev->speed; for(i = 0; (i < (sizeof(udev->subdevs)/sizeof(udev->subdevs[0]))) && (i < USB_MAX_DEVNAMES); i++) { if(udev->subdevs[i] && device_is_attached(udev->subdevs[i])) { strncpy(di->udi_devnames[i], device_get_nameunit(udev->subdevs[i]), USB_MAX_DEVNAMELEN); di->udi_devnames[i][USB_MAX_DEVNAMELEN-1] = 0; } /* else { di->udi_devnames[i][0] = 0; } */ } if(udev->hub) { for(i = 0; (i < (sizeof(di->udi_ports)/sizeof(di->udi_ports[0]))) && (i < udev->hub->hubdesc.bNbrPorts); i++) { p = &udev->hub->ports[i]; if(p->device) { err = p->device->address; } else { s = UGETW(p->status.wPortStatus); if (s & UPS_PORT_ENABLED) { err = USB_PORT_ENABLED; } else if (s & UPS_SUSPEND) { err = USB_PORT_SUSPENDED; } else if (s & UPS_PORT_POWER) { err = USB_PORT_POWERED; } else { err = USB_PORT_DISABLED; } } di->udi_ports[i] = err; } di->udi_nports = udev->hub->hubdesc.bNbrPorts; } return 0; } /* The following function will remove detached * devices from the interface list. This can * happen during USB device module unload. */ static void usbd_remove_detached_devices(struct usbd_device *udev) { device_t *subdev = udev->subdevs; device_t *subdev_end = udev->subdevs_end; uint8_t detached_first = 0; PRINTFN(3,("udev=%p\n", udev)); while (subdev < subdev_end) { if (subdev[0]) { if (device_is_attached(subdev[0]) == 0) { if (device_delete_child(device_get_parent(subdev[0]), subdev[0]) == 0) { subdev[0] = NULL; if (subdev == udev->subdevs) { detached_first = 1; } } else { /* Panic here, else one can get a double call to * device_detach(). USB devices should never fail * on detach! */ panic("device_delete_child() failed!\n"); } } } subdev++; } if (detached_first) { if ((udev->probed == USBD_PROBED_SPECIFIC_AND_FOUND) || (udev->probed == USBD_PROBED_GENERIC_AND_FOUND)) { /* The first and only device is gone. * Reset the "probed" variable. */ udev->probed = USBD_PROBED_NOTHING; } } return; } /* "usbd_probe_and_attach()" is called * from "usbd_new_device()" and "uhub_explore()" */ usbd_status usbd_probe_and_attach(device_t parent, int port, struct usbd_port *up) { struct usb_attach_arg uaa; struct usbd_device *udev = up->device; device_t bdev = NULL; usbd_status err = 0; u_int8_t config; u_int8_t i; up->last_refcount = usb_driver_added_refcount; if(udev == NULL) { PRINTF(("%s: port %d has no device\n", device_get_nameunit(parent), port)); return (USBD_INVAL); } usbd_remove_detached_devices(udev); bzero(&uaa, sizeof(uaa)); /* probe and attach */ uaa.device = udev; uaa.port = port; uaa.configno = -1; uaa.vendor = UGETW(udev->ddesc.idVendor); uaa.product = UGETW(udev->ddesc.idProduct); uaa.release = UGETW(udev->ddesc.bcdDevice); if((udev->probed == USBD_PROBED_SPECIFIC_AND_FOUND) || (udev->probed == USBD_PROBED_GENERIC_AND_FOUND)) { /* nothing more to probe */ goto done; } bdev = device_add_child(parent, NULL, -1); if(!bdev) { device_printf(udev->bus->bdev, "Device creation failed\n"); err = USBD_INVAL; goto done; } device_set_ivars(bdev, &uaa); device_quiet(bdev); if(udev->probed == USBD_PROBED_NOTHING) { /* first try device specific drivers */ PRINTF(("trying device specific drivers\n")); if(device_probe_and_attach(bdev) == 0) { device_set_ivars(bdev, NULL); /* no longer accessible */ udev->subdevs[0] = bdev; udev->probed = USBD_PROBED_SPECIFIC_AND_FOUND; bdev = 0; goto done; } PRINTF(("no device specific driver found; " "looping over %d configurations\n", udev->ddesc.bNumConfigurations)); } /* next try interface drivers */ if((udev->probed == USBD_PROBED_NOTHING) || (udev->probed == USBD_PROBED_IFACE_AND_FOUND)) { for(config = 0; config < udev->ddesc.bNumConfigurations; config++) { struct usbd_interface *iface; /* only set config index the first * time the devices are probed */ if(udev->probed == USBD_PROBED_NOTHING) { err = usbd_set_config_index(udev, config, 1); if(err) { device_printf(parent, "port %d, set config at addr %d " "failed, error=%s\n", port, udev->address, usbd_errstr(err)); goto done; } /* ``bNumInterface'' is checked * by ``usbd_set_config_index()'' * * ``USBD_CLR_IFACE_NO_PROBE()'' is run * by ``usbd_fill_iface_data()'', which * is called by ``usbd_set_config_index()'' */ } /* * else the configuration is already set */ uaa.configno = udev->cdesc->bConfigurationValue; uaa.ifaces_start = &udev->ifaces[0]; uaa.ifaces_end = &udev->ifaces[udev->cdesc->bNumInterface]; for(iface = uaa.ifaces_start; iface < uaa.ifaces_end; iface++) { uaa.iface = iface; uaa.iface_index = (i = (iface - &udev->ifaces[0])); if(uaa.iface_index >= (sizeof(udev->subdevs)/ sizeof(udev->subdevs[0]))) { device_printf(udev->bus->bdev, "Too many subdevices\n"); break; } if((USBD_GET_IFACE_NO_PROBE(udev, i) == 0) && (udev->subdevs[i] == NULL) && (device_probe_and_attach(bdev) == 0)) { /* "ivars" are no longer accessible: */ device_set_ivars(bdev, NULL); udev->subdevs[i] = bdev; udev->probed = USBD_PROBED_IFACE_AND_FOUND; bdev = 0; /* create another child for the next iface [if any] */ bdev = device_add_child(parent, NULL, -1); if(!bdev) { device_printf(udev->bus->bdev, "Device creation failed\n"); /* need to update "IFACE_NO_PROBE": */ break; } device_set_ivars(bdev, &uaa); device_quiet(bdev); } } if(udev->probed == USBD_PROBED_IFACE_AND_FOUND) { break; } } } if(udev->probed == USBD_PROBED_NOTHING) { /* set config index 0 */ err = usbd_set_config_index(udev, 0, 1); if(err) { device_printf(parent, "port %d, set config at addr %d " "failed, error=%s\n", port, udev->address, usbd_errstr(err)); goto done; } PRINTF(("no interface drivers found\n")); /* finally try the generic driver */ uaa.iface = NULL; uaa.iface_index = 0; uaa.ifaces_start = NULL; uaa.ifaces_end = NULL; uaa.usegeneric = 1; uaa.configno = -1; if(device_probe_and_attach(bdev) == 0) { device_set_ivars(bdev, NULL); /* no longer accessible */ udev->subdevs[0] = bdev; udev->probed = USBD_PROBED_GENERIC_AND_FOUND; bdev = 0; goto done; } /* * Generic attach failed. * The device is left as it is. * It has no driver, but is fully operational. */ PRINTF(("generic attach failed\n")); } done: if(bdev) { /* remove the last created child; it is unused */ device_delete_child(parent, bdev); } return err; } /* * Called when a new device has been put in the powered state, * but not yet in the addressed state. * Get initial descriptor, set the address, get full descriptor, * and attach a driver. */ usbd_status usbd_new_device(device_t parent, struct usbd_bus *bus, int depth, int speed, int port, struct usbd_port *up) { struct usbd_device *adev; struct usbd_device *udev; struct usbd_device *hub; usb_port_status_t ps; usbd_status err = 0; int addr; int i; PRINTF(("bus=%p port=%d depth=%d speed=%d\n", bus, port, depth, speed)); /* find unused address */ addr = USB_MAX_DEVICES; #if (USB_MAX_DEVICES == 0) #error "(USB_MAX_DEVICES == 0)" #endif while(addr--) { if(addr == 0) { /* address 0 is always unused */ device_printf(bus->bdev, "No free USB addresses, " "new device ignored.\n"); return (USBD_NO_ADDR); } if(bus->devices[addr] == 0) { break; } } udev = malloc(sizeof(udev[0]), M_USB, M_NOWAIT|M_ZERO); if(udev == NULL) { return (USBD_NOMEM); } up->device = udev; /* set up default endpoint descriptor */ udev->default_ep_desc.bLength = USB_ENDPOINT_DESCRIPTOR_SIZE; udev->default_ep_desc.bDescriptorType = UDESC_ENDPOINT; udev->default_ep_desc.bEndpointAddress = USB_CONTROL_ENDPOINT; udev->default_ep_desc.bmAttributes = UE_CONTROL; USETW(udev->default_ep_desc.wMaxPacketSize, USB_MAX_IPACKET); udev->default_ep_desc.bInterval = 0; udev->bus = bus; udev->quirks = &usbd_no_quirk; udev->address = USB_START_ADDR; udev->ddesc.bMaxPacketSize = 0; udev->depth = depth; udev->powersrc = up; udev->myhub = up->parent; hub = up->parent; if(hub) { if(speed > hub->speed) { #ifdef USB_DEBUG printf("%s: maxium speed of attached " "device, %d, is higher than speed " "of parent HUB, %d.\n", __FUNCTION__, speed, hub->speed); #endif /* speed down * (else there is trouble setting * up the right transfer methods) */ speed = hub->speed; } } adev = udev; while(hub && (hub->speed != USB_SPEED_HIGH)) { adev = hub; hub = hub->myhub; } if(hub) { for(i = 0; i < hub->hub->hubdesc.bNbrPorts; i++) { if(hub->hub->ports[i].device == adev) { udev->myhsport = &hub->hub->ports[i]; break; } } } udev->speed = speed; udev->langid = USBD_NOLANG; /* usb_cookie_no is used by "usb.c" */ static u_int32_t usb_cookie_no = 0; udev->cookie.cookie = ++usb_cookie_no; /* init the default pipe */ usbd_fill_pipe_data(udev, 0, &udev->default_ep_desc, &udev->default_pipe); /* Set the address. Do this early; some devices need that. * Try a few times in case the device is slow (i.e. outside specs.) */ for (i = 0; i < 15; i++) { err = usbreq_set_address(udev, addr); if(!err) { break; } usbd_delay_ms(udev, 200); if(((i & 3) == 3) && (up->parent)) { PRINTF(("set address %d " "failed - trying a port reset\n", addr)); usbreq_reset_port(up->parent, port, &ps); } } if(err) { PRINTF(("set address %d failed\n", addr)); err = USBD_SET_ADDR_FAILED; goto done; } /* allow device time to set new address */ usbd_delay_ms(udev, USB_SET_ADDRESS_SETTLE); udev->address = addr; /* new device address now */ bus->devices[addr] = udev; /* get the first 8 bytes of the device descriptor */ err = usbreq_get_desc(udev, UDESC_DEVICE, 0, USB_MAX_IPACKET, &udev->ddesc, 0); if(err) { PRINTF(("addr=%d, getting first desc failed\n", udev->address)); goto done; } if(speed == USB_SPEED_HIGH) { /* max packet size must be 64 (sec 5.5.3) */ if(udev->ddesc.bMaxPacketSize != USB_2_MAX_CTRL_PACKET) { #ifdef DIAGNOSTIC printf("%s: addr=%d bad max packet size\n", __FUNCTION__, udev->address); #endif udev->ddesc.bMaxPacketSize = USB_2_MAX_CTRL_PACKET; } } if(udev->ddesc.bMaxPacketSize == 0) { #ifdef USB_DEBUG printf("%s: addr=%d invalid bMaxPacketSize!\n", __FUNCTION__, udev->address); #endif /* avoid division by zero */ udev->ddesc.bMaxPacketSize = USB_MAX_IPACKET; } PRINTF(("adding unit addr=%d, rev=%02x, class=%d, " "subclass=%d, protocol=%d, maxpacket=%d, len=%d, speed=%d\n", udev->address, UGETW(udev->ddesc.bcdUSB), udev->ddesc.bDeviceClass, udev->ddesc.bDeviceSubClass, udev->ddesc.bDeviceProtocol, udev->ddesc.bMaxPacketSize, udev->ddesc.bLength, udev->speed)); if(udev->ddesc.bDescriptorType != UDESC_DEVICE) { /* illegal device descriptor */ PRINTF(("illegal descriptor %d\n", udev->ddesc.bDescriptorType)); err = USBD_INVAL; goto done; } if(udev->ddesc.bLength < USB_DEVICE_DESCRIPTOR_SIZE) { PRINTF(("bad length %d\n", udev->ddesc.bLength)); err = USBD_INVAL; goto done; } USETW(udev->default_ep_desc.wMaxPacketSize, udev->ddesc.bMaxPacketSize); /* get the full device descriptor */ err = usbreq_get_device_desc(udev, &udev->ddesc); if(err) { PRINTF(("addr=%d, getting full desc failed\n", udev->address)); goto done; } /* figure out what's wrong with this device */ udev->quirks = usbd_find_quirk(&udev->ddesc); /* assume 100mA bus powered for now. Changed when configured. */ udev->power = USB_MIN_POWER; udev->self_powered = 0; /* buffer serial number */ (void) usbreq_get_string_any (udev, udev->ddesc.iSerialNumber, &udev->serial[0], sizeof(udev->serial)); /* check serial number format */ for(i = 0;;i++) { if(udev->serial[i] == '\0') break; if(udev->serial[i] == '\"') udev->serial[i] = ' '; if(udev->serial[i] == '\n') udev->serial[i] = ' '; } PRINTF(("new dev (addr %d), udev=%p, parent=%p\n", udev->address, udev, parent)); err = usbd_probe_and_attach(parent, port, up); done: if(err) { /* remove device and sub-devices */ usbd_free_device(up, 1); } else { usbd_add_dev_event(USB_EVENT_DEVICE_ATTACH, udev); } return(err); } /* called when a port has been disconnected * * The general mechanism for detaching: * * The drivers should use a static softc or a softc which is not freed * immediately, so that calls to routines which are about to access * the softc, does not access freed memory. * * The drivers mutex should also be available for some time after * detach. * * The drivers should have a detach flag which is set when the driver * is detached. The detach flag is checked after locking drivers mutex * and after waking up from sleep. When the detach flag is set, the * driver must unlock drivers mutex and exit. */ void usbd_free_device(struct usbd_port *up, u_int8_t free_subdev) { struct usbd_device *udev = up->device; device_t *subdev = &udev->subdevs[0]; device_t *subdev_end = &udev->subdevs_end[0]; int error = 0; /* mtx_assert() */ if(udev == NULL) { /* already freed */ return; } PRINTFN(3,("up=%p udev=%p port=%d; " "disconnect subdevs\n", up, udev, up->portno)); while(subdev < subdev_end) { if(subdev[0] && free_subdev) { device_printf(subdev[0], "at %s ", device_get_nameunit (device_get_parent(subdev[0]))); if(up->portno != 0) { printf("port %d ", up->portno); } printf("(addr %d) disconnected\n", udev->address); /* device_delete_child() will detach all sub-devices ! */ if(device_delete_child (device_get_parent(subdev[0]), subdev[0])) { /* if detach fails sub-devices will still * be referring to the udev structure * which cannot be freed */ device_printf(subdev[0], "detach failed " "(please ensure that this " "device driver supports detach)\n"); error = ENXIO; } } /* always clear subdev[0], * because it might be used by * usbd_add_dev_event() */ subdev[0] = NULL; subdev++; } /* issue detach event and free address */ if(udev->bus != NULL) { usbd_add_dev_event(USB_EVENT_DEVICE_DETACH, udev); /* NOTE: address 0 is always unused */ udev->bus->devices[udev->address] = 0; } usbd_free_iface_data(udev); if(error) { panic("%s: some USB devices would not detach\n", __FUNCTION__); } /* free device */ free(udev, M_USB); up->device = 0; return; } void usb_detach_wait(device_t dev) { PRINTF(("waiting for %s\n", device_get_nameunit(dev))); /* XXX should use msleep */ if(tsleep(dev, PZERO, "usbdet", hz * 60)) { device_printf(dev, "didn't detach\n"); } PRINTF(("%s done\n", device_get_nameunit(dev))); return; } void usb_detach_wakeup(device_t dev) { PRINTF(("for %s\n", device_get_nameunit(dev))); wakeup(dev); return; } struct usbd_interface * usbd_get_iface(struct usbd_device *udev, u_int8_t iface_index) { struct usbd_interface *iface = &udev->ifaces[iface_index]; if((iface < &udev->ifaces[0]) || (iface >= &udev->ifaces_end[0]) || (udev->cdesc == NULL) || (iface_index >= udev->cdesc->bNumInterface)) { return NULL; } return iface; } void usbd_set_desc(device_t dev, struct usbd_device *udev) { u_int8_t devinfo[256]; usbd_devinfo(udev, 1, devinfo, sizeof(devinfo)); device_set_desc_copy(dev, devinfo); device_printf(dev, "<%s>\n", devinfo); return; } /*------------------------------------------------------------------------------* * allocate mbufs to an usbd interface queue * * returns a pointer that eventually should be passed to "free()" *------------------------------------------------------------------------------*/ void * usbd_alloc_mbufs(struct malloc_type *type, struct usbd_ifqueue *ifq, u_int32_t block_size, u_int16_t block_number) { struct usbd_mbuf *m_ptr; u_int8_t *data_ptr; void *free_ptr = NULL; u_int32_t alloc_size; /* align data */ block_size += ((-block_size) & (USB_HOST_ALIGN-1)); if (block_number && block_size) { alloc_size = (block_size + sizeof(struct usbd_mbuf)) * block_number; free_ptr = malloc(alloc_size, type, M_WAITOK|M_ZERO); if (free_ptr == NULL) { goto done; } m_ptr = free_ptr; data_ptr = (void *)(m_ptr + block_number); while(block_number--) { m_ptr->cur_data_ptr = m_ptr->min_data_ptr = data_ptr; m_ptr->cur_data_len = m_ptr->max_data_len = block_size; USBD_IF_ENQUEUE(ifq, m_ptr); m_ptr++; data_ptr += block_size; } } done: return free_ptr; } /*------------------------------------------------------------------------------* * usbd_get_page - lookup DMA-able memory for the given offset *------------------------------------------------------------------------------*/ void usbd_get_page(struct usbd_page_cache *cache, u_int32_t offset, struct usbd_page_search *res) { struct usbd_page *page; offset += cache->page_offset_buf; if ((offset < cache->page_offset_cur) || (cache->page_cur == NULL)) { /* reset the page search */ cache->page_cur = cache->page_start; cache->page_offset_cur = 0; } offset -= cache->page_offset_cur; page = cache->page_cur; while (1) { if (page >= cache->page_end) { res->length = 0; res->buffer = NULL; res->physaddr = 0; break; } if (offset < page->length) { /* found the offset on a page */ res->length = page->length - offset; res->buffer = ADD_BYTES(page->buffer, offset); res->physaddr = page->physaddr + offset; break; } /* get the next page */ offset -= page->length; cache->page_offset_cur += page->length; page++; cache->page_cur = page; } return; } /*------------------------------------------------------------------------------* * usbd_copy_in - copy directly to DMA-able memory *------------------------------------------------------------------------------*/ void usbd_copy_in(struct usbd_page_cache *cache, u_int32_t offset, const void *ptr, u_int32_t len) { struct usbd_page_search res; while (len) { usbd_get_page(cache, offset, &res); if (res.length == 0) { panic("%s:%d invalid offset!\n", __FUNCTION__, __LINE__); } if (res.length > len) { res.length = len; } bcopy(ptr, res.buffer, res.length); offset += res.length; len -= res.length; ptr = ((const u_int8_t *)ptr) + res.length; } return; } /*------------------------------------------------------------------------------* * usbd_m_copy_in - copy a mbuf chain directly into DMA-able memory *------------------------------------------------------------------------------*/ struct usbd_m_copy_in_arg { struct usbd_page_cache *cache; u_int32_t dst_offset; }; static int32_t #ifdef __FreeBSD__ usbd_m_copy_in_cb(void *arg, void *src, u_int32_t count) #else usbd_m_copy_in_cb(void *arg, caddr_t src, u_int32_t count) #endif { register struct usbd_m_copy_in_arg *ua = arg; usbd_copy_in(ua->cache, ua->dst_offset, src, count); ua->dst_offset += count; return 0; } void usbd_m_copy_in(struct usbd_page_cache *cache, u_int32_t dst_offset, struct mbuf *m, u_int32_t src_offset, u_int32_t src_len) { struct usbd_m_copy_in_arg arg = { cache, dst_offset }; register int error; error = m_apply(m, src_offset, src_len, &usbd_m_copy_in_cb, &arg); return; } /*------------------------------------------------------------------------------* * usbd_copy_out - copy directly from DMA-able memory *------------------------------------------------------------------------------*/ void usbd_copy_out(struct usbd_page_cache *cache, u_int32_t offset, void *ptr, u_int32_t len) { struct usbd_page_search res; while (len) { usbd_get_page(cache, offset, &res); if (res.length == 0) { panic("%s:%d invalid offset!\n", __FUNCTION__, __LINE__); } if (res.length > len) { res.length = len; } bcopy(res.buffer, ptr, res.length); offset += res.length; len -= res.length; ptr = ADD_BYTES(ptr, res.length); } return; } /*------------------------------------------------------------------------------* * usbd_bzero - zero DMA-able memory *------------------------------------------------------------------------------*/ void usbd_bzero(struct usbd_page_cache *cache, u_int32_t offset, u_int32_t len) { struct usbd_page_search res; while (len) { usbd_get_page(cache, offset, &res); if (res.length == 0) { panic("%s:%d invalid offset!\n", __FUNCTION__, __LINE__); } if (res.length > len) { res.length = len; } bzero(res.buffer, res.length); offset += res.length; len -= res.length; } return; } /*------------------------------------------------------------------------------* * usbd_page_alloc - allocate multiple DMA-able memory pages * * return values: * 1: failure * 0: success *------------------------------------------------------------------------------*/ u_int8_t usbd_page_alloc(bus_dma_tag_t tag, struct usbd_page *page, u_int32_t npages) { u_int32_t x; void *ptr; for (x = 0; x < npages; x++) { repeat: ptr = usbd_mem_alloc_sub(tag, page + x, USB_PAGE_SIZE, USB_PAGE_SIZE); if (ptr == NULL) { while(x--) { usbd_mem_free_sub(page + x); } return 1; /* failure */ } else { if ((page + x)->physaddr == 0) { /* at least the OHCI controller * gives special meaning to * physaddr == 0, so discard * that page if it gets here: */ printf("%s:%d: Discarded memory " "page with physaddr=0!\n", __FUNCTION__, __LINE__); goto repeat; } } } return 0; /* success */ } /*------------------------------------------------------------------------------* * usbd_page_free - free multiple DMA-able memory pages *------------------------------------------------------------------------------*/ void usbd_page_free(struct usbd_page *page, u_int32_t npages) { while(npages--) { usbd_mem_free_sub(page + npages); } return; } /*------------------------------------------------------------------------------* * usbd_page_get_buf - get virtual buffer *------------------------------------------------------------------------------*/ void * usbd_page_get_buf(struct usbd_page *page, u_int32_t size) { page += (size / USB_PAGE_SIZE); size &= (USB_PAGE_SIZE-1); return ADD_BYTES(page->buffer,size); } /*------------------------------------------------------------------------------* * usbd_page_get_phy - get physical buffer *------------------------------------------------------------------------------*/ bus_size_t usbd_page_get_phy(struct usbd_page *page, u_int32_t size) { page += (size / USB_PAGE_SIZE); size &= (USB_PAGE_SIZE-1); return (page->physaddr + size); } /*------------------------------------------------------------------------------* * usbd_page_set_start *------------------------------------------------------------------------------*/ void usbd_page_set_start(struct usbd_page_cache *pc, struct usbd_page *page_ptr, u_int32_t size) { pc->page_start = page_ptr + (size / USB_PAGE_SIZE); pc->page_offset_buf = (size % USB_PAGE_SIZE); return; } /*------------------------------------------------------------------------------* * usbd_page_set_end *------------------------------------------------------------------------------*/ void usbd_page_set_end(struct usbd_page_cache *pc, struct usbd_page *page_ptr, u_int32_t size) { pc->page_end = (page_ptr + ((size + USB_PAGE_SIZE -1) / USB_PAGE_SIZE)); return; } /*------------------------------------------------------------------------------* * usbd_page_fit_obj - fit object function *------------------------------------------------------------------------------*/ u_int32_t usbd_page_fit_obj(struct usbd_page *page, u_int32_t size, u_int32_t obj_len) { u_int32_t adj; if (obj_len > USB_PAGE_SIZE) { panic("%s:%d Too large object, %d bytes, will " "not fit on a USB page, %d bytes!\n", __FUNCTION__, __LINE__, obj_len, USB_PAGE_SIZE); } if (obj_len > (USB_PAGE_SIZE - (size & (USB_PAGE_SIZE-1)))) { /* adjust offset to the beginning * of the next page: */ adj = ((-size) & (USB_PAGE_SIZE-1)); if (page) { /* adjust the size of the * current page, so that the * objects follow back to back! */ (page + (size/USB_PAGE_SIZE))->length -= adj; } } else { adj = 0; } return adj; } /*------------------------------------------------------------------------------* * usbd_mem_alloc - allocate DMA-able memory *------------------------------------------------------------------------------*/ void * usbd_mem_alloc(bus_dma_tag_t parent, u_int32_t size, u_int8_t align_power) { bus_dma_tag_t tag; struct usbd_page temp; u_int32_t alignment = (1 << align_power); void *ptr; size += (-size) & (USB_HOST_ALIGN-1); size += sizeof(temp); tag = usbd_dma_tag_alloc(parent, size, alignment); if (tag == NULL) { return NULL; } ptr = usbd_mem_alloc_sub(tag, &temp, size, alignment); if (ptr == NULL) { usbd_dma_tag_free(tag); return NULL; } size -= sizeof(temp); bcopy(&temp, ADD_BYTES(ptr,size), sizeof(temp)); return ptr; } /*------------------------------------------------------------------------------* * usbd_mem_vtophys - get the physical address of DMA-able memory *------------------------------------------------------------------------------*/ bus_size_t usbd_mem_vtophys(void *ptr, u_int32_t size) { struct usbd_page *page; size += (-size) & (USB_HOST_ALIGN-1); page = ADD_BYTES(ptr, size); return page->physaddr; } /*------------------------------------------------------------------------------* * usbd_mem_free - free DMA-able memory *------------------------------------------------------------------------------*/ void usbd_mem_free(void *ptr, u_int32_t size) { struct usbd_page *page; bus_dma_tag_t tag; size += (-size) & (USB_HOST_ALIGN-1); page = ADD_BYTES(ptr, size); tag = page->tag; usbd_mem_free_sub(page); usbd_dma_tag_free(tag); return; } #ifdef __FreeBSD__ /*------------------------------------------------------------------------------* * bus_dmamap_load_callback *------------------------------------------------------------------------------*/ static void bus_dmamap_load_callback(void *arg, bus_dma_segment_t *segs, int nseg, int error) { *((bus_size_t *)arg) = nseg ? segs->ds_addr : 0; if(error) { printf("%s: %s: error=%d\n", __FILE__, __FUNCTION__, error); } return; } /*------------------------------------------------------------------------------* * usbd_dma_tag_alloc - allocate a bus-DMA tag *------------------------------------------------------------------------------*/ bus_dma_tag_t usbd_dma_tag_alloc(bus_dma_tag_t parent, u_int32_t size, u_int32_t alignment) { bus_dma_tag_t tag; if(bus_dma_tag_create ( /* parent */parent, /* alignment */alignment, /* boundary */0, /* lowaddr */BUS_SPACE_MAXADDR_32BIT, /* highaddr */BUS_SPACE_MAXADDR, /* filter */NULL, /* filterarg */NULL, /* maxsize */size, /* nsegments */1, /* maxsegsz */size, /* flags */0, /* lock */NULL, /* */NULL, &tag)) { tag = NULL; } return tag; } /*------------------------------------------------------------------------------* * usbd_dma_tag_free - free a bus-DMA tag *------------------------------------------------------------------------------*/ void usbd_dma_tag_free(bus_dma_tag_t tag) { bus_dma_tag_destroy(tag); return; } /*------------------------------------------------------------------------------* * usbd_mem_alloc_sub - allocate DMA-able memory *------------------------------------------------------------------------------*/ void * usbd_mem_alloc_sub(bus_dma_tag_t tag, struct usbd_page *page, u_int32_t size, u_int32_t alignment) { bus_dmamap_t map; bus_size_t physaddr = 0; void *ptr; if(bus_dmamem_alloc (tag, &ptr, (BUS_DMA_WAITOK|BUS_DMA_COHERENT), &map)) { return NULL; } if(bus_dmamap_load (tag, map, ptr, size, &bus_dmamap_load_callback, &physaddr, (BUS_DMA_WAITOK|BUS_DMA_COHERENT))) { bus_dmamem_free(tag, ptr, map); return NULL; } page->tag = tag; page->map = map; page->physaddr = physaddr; page->buffer = ptr; page->length = size; bzero(ptr, size); #ifdef USB_DEBUG if(usbdebug > 14) { printf("%s: %p, %d bytes, phys=%p\n", __FUNCTION__, ptr, size, ((char *)0) + physaddr); } #endif return ptr; } /*------------------------------------------------------------------------------* * usbd_mem_free_sub - free DMA-able memory *------------------------------------------------------------------------------*/ void usbd_mem_free_sub(struct usbd_page *page) { /* NOTE: make a copy of "tag", "map", * and "buffer" in case "page" is part * of the allocated memory: */ bus_dma_tag_t tag = page->tag; bus_dmamap_t map = page->map; void *ptr = page->buffer; bus_dmamap_unload(tag, map); bus_dmamem_free(tag, ptr, map); #ifdef USB_DEBUG if(usbdebug > 14) { printf("%s: %p\n", __FUNCTION__, ptr); } #endif return; } #endif #ifdef __NetBSD__ bus_dma_tag_t usbd_dma_tag_alloc(bus_dma_tag_t parent, u_int32_t size, u_int32_t alignment) { /* FreeBSD specific */ return parent; } void usbd_dma_tag_free(bus_dma_tag_t tag) { return; } void * usbd_mem_alloc_sub(bus_dma_tag_t tag, struct usbd_page *page, u_int32_t size, u_int32_t alignment) { caddr_t ptr = NULL; page->tag = tag; page->seg_count = 1; if(bus_dmamem_alloc(page->tag, size, alignment, 0, &page->seg, 1, &page->seg_count, BUS_DMA_WAITOK)) { goto done_4; } if(bus_dmamem_map(page->tag, &page->seg, page->seg_count, size, &ptr, BUS_DMA_WAITOK|BUS_DMA_COHERENT)) { goto done_3; } if(bus_dmamap_create(page->tag, size, 1, size, 0, BUS_DMA_WAITOK, &page->map)) { goto done_2; } if(bus_dmamap_load(page->tag, page->map, ptr, size, NULL, BUS_DMA_WAITOK)) { goto done_1; } page->physaddr = page->map->dm_segs[0].ds_addr; page->buffer = ptr; page->length = size; bzero(ptr, size); #ifdef USB_DEBUG if(usbdebug > 14) { printf("%s: %p, %d bytes, phys=%p\n", __FUNCTION__, ptr, size, ((char *)0) + page->physaddr); } #endif return ptr; done_1: bus_dmamap_destroy(page->tag, page->map); done_2: bus_dmamem_unmap(page->tag, ptr, size); done_3: bus_dmamem_free(page->tag, &page->seg, page->seg_count); done_4: return NULL; } void usbd_mem_free_sub(struct usbd_page *page) { /* NOTE: make a copy of "tag", "map", * and "buffer" in case "page" is part * of the allocated memory: */ struct usbd_page temp = *page; bus_dmamap_unload(temp.tag, temp.map); bus_dmamap_destroy(temp.tag, temp.map); bus_dmamem_unmap(temp.tag, temp.buffer, temp.length); bus_dmamem_free(temp.tag, &temp.seg, temp.seg_count); #ifdef USB_DEBUG if(usbdebug > 14) { printf("%s: %p\n", __FUNCTION__, temp.buffer); } #endif return; } #endif /*------------------------------------------------------------------------------* * usbd_std_transfer_setup - standard transfer setup *------------------------------------------------------------------------------*/ void usbd_std_transfer_setup(struct usbd_xfer *xfer, const struct usbd_config *setup, u_int16_t max_packet_size, u_int16_t max_frame_size) { __callout_init_mtx(&xfer->timeout_handle, xfer->usb_mtx, CALLOUT_RETURNUNLOCKED); xfer->flags = setup->flags; xfer->nframes = setup->frames; xfer->timeout = setup->timeout; xfer->callback = setup->callback; xfer->interval = setup->interval; xfer->endpoint = xfer->pipe->edesc->bEndpointAddress; xfer->max_packet_size = UGETW(xfer->pipe->edesc->wMaxPacketSize); xfer->length = setup->bufsize; if(xfer->interval == 0) { xfer->interval = xfer->pipe->edesc->bInterval; } if(xfer->interval == 0) { /* one is the smallest interval */ xfer->interval = 1; } /* wMaxPacketSize is also checked by "usbd_fill_iface_data()" */ if (xfer->max_packet_size == 0) { xfer->max_packet_size = 8; } if (xfer->max_packet_size > max_packet_size) { xfer->max_packet_size = max_packet_size; } /* frame_size: isochronous frames only */ xfer->max_frame_size = max_frame_size; if (xfer->length == 0) { xfer->length = xfer->max_packet_size; if (xfer->nframes) { xfer->length *= xfer->nframes; } if (setup->sub_frames) { xfer->length *= setup->sub_frames; } } return; } /*------------------------------------------------------------------------------* * usbd_make_str_desc - convert an ASCII string into a UNICODE string *------------------------------------------------------------------------------*/ u_int8_t usbd_make_str_desc(void *ptr, u_int16_t max_len, const char *s) { usb_string_descriptor_t *p = ptr; u_int8_t totlen; int32_t j; if (max_len < 2) { /* invalid length */ return 0; } max_len = ((max_len / 2) - 1); j = strlen(s); if (j < 0) { j = 0; } if (j > 126) { j = 126; } if (max_len > j) { max_len = j; } totlen = (max_len + 1) * 2; p->bLength = totlen; p->bDescriptorType = UDESC_STRING; while (max_len--) { USETW2(p->bString[max_len], 0, s[max_len]); } return totlen; } /*---------------------------------------------------------------------------* * mtx_drop_recurse - drop mutex recurse level *---------------------------------------------------------------------------*/ u_int32_t mtx_drop_recurse(struct mtx *mtx) { u_int32_t recurse_level = mtx->mtx_recurse; u_int32_t recurse_curr = recurse_level; mtx_assert(mtx, MA_OWNED); while(recurse_curr--) { mtx_unlock(mtx); } return recurse_level; } /*---------------------------------------------------------------------------* * mtx_pickup_recurse - pickup mutex recurse level *---------------------------------------------------------------------------*/ void mtx_pickup_recurse(struct mtx *mtx, u_int32_t recurse_level) { mtx_assert(mtx, MA_OWNED); while(recurse_level--) { mtx_lock(mtx); } return; } /*---------------------------------------------------------------------------* * usbd_config_thread *---------------------------------------------------------------------------*/ static void usbd_config_td_thread(void *arg) { struct usbd_config_td *ctd = arg; struct usbd_config_td_item *item; struct usbd_mbuf *m; register int error; mtx_lock(ctd->p_mtx); while(1) { if (ctd->flag_config_td_gone) { break; } USBD_IF_DEQUEUE(&(ctd->cmd_used), m); if (m) { item = (void *)(m->cur_data_ptr); (item->command_func) (ctd->p_softc, (void *)(item+1), item->command_ref); USBD_IF_ENQUEUE(&(ctd->cmd_free), m); continue; } if (ctd->p_end_of_commands) { (ctd->p_end_of_commands)(ctd->p_softc); } ctd->flag_config_td_sleep = 1; error = msleep(&(ctd->wakeup_config_td), ctd->p_mtx, 0, "cfg td sleep", 0); ctd->flag_config_td_sleep = 0; } ctd->config_thread = NULL; wakeup(&(ctd->wakeup_config_td_gone)); mtx_unlock(ctd->p_mtx); kthread_exit(0); return; } /*---------------------------------------------------------------------------* * usbd_config_td_setup * * NOTE: the structure pointed to by "ctd" must be zeroed before calling * this function! * * Return values: * 0: success * else: failure *---------------------------------------------------------------------------*/ u_int8_t usbd_config_td_setup(struct usbd_config_td *ctd, void *priv_sc, struct mtx *priv_mtx, usbd_config_td_config_copy_t *p_func_cc, usbd_config_td_end_of_commands_t *p_func_eoc, u_int16_t item_size, u_int16_t item_count) { ctd->p_mtx = priv_mtx; ctd->p_softc = priv_sc; ctd->p_config_copy = p_func_cc; ctd->p_end_of_commands = p_func_eoc; if (item_count >= 256) { PRINTFN(0,("too many items!\n")); goto error; } ctd->p_cmd_queue = usbd_alloc_mbufs(M_DEVBUF, &(ctd->cmd_free), (sizeof(struct usbd_config_td_item) + item_size), item_count); if (ctd->p_cmd_queue == NULL) { PRINTFN(0,("unable to allocate memory " "for command queue!\n")); goto error; } if (usb_kthread_create1 (&usbd_config_td_thread, ctd, &(ctd->config_thread), "usbd config thread")) { PRINTFN(0,("unable to create config thread!\n")); ctd->config_thread = NULL; goto error; } return 0; error: usbd_config_td_unsetup(ctd); return 1; } /*---------------------------------------------------------------------------* * usbd_config_td_dummy_cmd *---------------------------------------------------------------------------*/ static void usbd_config_td_dummy_cmd(struct usbd_config_td_softc *sc, struct usbd_config_td_cc *cc, u_int16_t reference) { return; } /*---------------------------------------------------------------------------* * usbd_config_td_stop * * NOTE: If the structure pointed to by "ctd" is all zero, * this function does nothing. *---------------------------------------------------------------------------*/ void usbd_config_td_stop(struct usbd_config_td *ctd) { register int error; while (ctd->config_thread) { mtx_assert(ctd->p_mtx, MA_OWNED); ctd->flag_config_td_gone = 1; usbd_config_td_queue_command(ctd, &usbd_config_td_dummy_cmd, 0); if (cold) { panic("%s:%d: cannot stop config thread!\n", __FUNCTION__, __LINE__); } error = msleep(&(ctd->wakeup_config_td_gone), ctd->p_mtx, 0, "wait config TD", 0); } return; } /*---------------------------------------------------------------------------* * usbd_config_td_unsetup * * NOTE: If the structure pointed to by "ctd" is all zero, * this function does nothing. *---------------------------------------------------------------------------*/ void usbd_config_td_unsetup(struct usbd_config_td *ctd) { if (ctd->p_mtx) { mtx_lock(ctd->p_mtx); usbd_config_td_stop(ctd); mtx_unlock(ctd->p_mtx); } if (ctd->p_cmd_queue) { free(ctd->p_cmd_queue, M_DEVBUF); ctd->p_cmd_queue = NULL; } return; } /*---------------------------------------------------------------------------* * usbd_config_td_queue_command *---------------------------------------------------------------------------*/ void usbd_config_td_queue_command(struct usbd_config_td *ctd, usbd_config_td_command_t *command_func, u_int16_t command_ref) { struct usbd_config_td_item *item; struct usbd_mbuf *m; int32_t qlen; mtx_assert(ctd->p_mtx, MA_OWNED); /* * first check if the command was * already queued, and if so, remove * it from the queue: */ qlen = USBD_IF_QLEN(&(ctd->cmd_used)); while (qlen--) { USBD_IF_DEQUEUE(&(ctd->cmd_used), m); if (m == NULL) { /* should not happen */ break; } item = (void *)(m->cur_data_ptr); if ((item->command_func == command_func) && (item->command_ref == command_ref)) { USBD_IF_ENQUEUE(&(ctd->cmd_free), m); } else { USBD_IF_ENQUEUE(&(ctd->cmd_used), m); } } /* first call to command function * (do this before calling the * config copy function, so that * the immediate part of the command * function gets a chance to change * the config before it is copied) */ (command_func)(ctd->p_softc, NULL, command_ref); USBD_IF_DEQUEUE(&(ctd->cmd_free), m); if (m == NULL) { /* should not happen */ panic("%s:%d: out of memory!\n", __FUNCTION__, __LINE__); } USBD_MBUF_RESET(m); item = (void *)(m->cur_data_ptr); if (ctd->p_config_copy) { (ctd->p_config_copy)(ctd->p_softc, (void *)(item+1), command_ref); } item->command_func = command_func; item->command_ref = command_ref; USBD_IF_ENQUEUE(&(ctd->cmd_used), m); if (ctd->flag_config_td_sleep) { ctd->flag_config_td_sleep = 0; wakeup(&(ctd->wakeup_config_td)); } return; } /*---------------------------------------------------------------------------* * usbd_config_td_is_gone * * Return values: * 0: config thread is running * else: config thread is gone *---------------------------------------------------------------------------*/ u_int8_t usbd_config_td_is_gone(struct usbd_config_td *ctd) { mtx_assert(ctd->p_mtx, MA_OWNED); return ctd->flag_config_td_gone ? 1 : 0; } /*---------------------------------------------------------------------------* * usbd_config_td_sleep * * NOTE: this function can only be called from the config thread * * Return values: * 0: normal delay * else: config thread is gone *---------------------------------------------------------------------------*/ u_int8_t usbd_config_td_sleep(struct usbd_config_td *ctd, u_int32_t timeout) { register int error; u_int8_t is_gone = usbd_config_td_is_gone(ctd); u_int32_t level; if (is_gone) { goto done; } if (timeout == 0) { /* zero means no timeout, * so avoid that by setting * timeout to one: */ timeout = 1; } level = mtx_drop_recurse(ctd->p_mtx); error = msleep(ctd, ctd->p_mtx, 0, "config td sleep", timeout); mtx_pickup_recurse(ctd->p_mtx, level); done: return is_gone; } /*---------------------------------------------------------------------------* * usbd_ether_get_mbuf - get a new ethernet mbuf *---------------------------------------------------------------------------*/ struct mbuf * usbd_ether_get_mbuf(void) { register struct mbuf *m_new; m_new = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); if (m_new) { m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; m_adj(m_new, ETHER_ALIGN); } return (m_new); } pwcbsd/usb/usb_subr.h000644 000423 000000 00000077246 10551670754 015416 0ustar00luigiwheel000000 000000 /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifndef _USB_SUBR_H_ #define _USB_SUBR_H_ #define USBD_STATUS_DESC(enum,value) #enum #define USBD_STATUS(m)\ m(USBD_NORMAL_COMPLETION,=0 /* must be zero*/)\ /* errors */\ m(USBD_PENDING_REQUESTS,)\ m(USBD_NOT_STARTED,)\ m(USBD_INVAL,)\ m(USBD_NOMEM,)\ m(USBD_CANCELLED,)\ m(USBD_BAD_ADDRESS,)\ m(USBD_BAD_BUFSIZE,)\ m(USBD_BAD_FLAG,)\ m(USBD_NO_CALLBACK,)\ m(USBD_SYNC_TRANSFER_MUST_USE_DEFAULT_CALLBACK,)\ m(USBD_IN_USE,)\ m(USBD_NO_ADDR,)\ m(USBD_NO_PIPE,)\ m(USBD_ZERO_FRAMES_IN_ISOC_MODE,)\ m(USBD_SET_ADDR_FAILED,)\ m(USBD_NO_POWER,)\ m(USBD_TOO_DEEP,)\ m(USBD_IOERROR,)\ m(USBD_NOT_CONFIGURED,)\ m(USBD_TIMEOUT,)\ m(USBD_SHORT_XFER,)\ m(USBD_STALLED,)\ m(USBD_INTERRUPTED,)\ /**/ MAKE_ENUM(USBD_STATUS, N_USBD_STATUS); struct usbd_xfer; struct usbd_pipe; struct usbd_bus; struct usbd_config; struct usbd_device; struct usbd_interface; struct usbd_memory_info; struct usbd_ifqueue; struct __callout; struct module; struct malloc_type; struct proc; typedef u_int8_t usbd_status; typedef void (*usbd_callback_t)(struct usbd_xfer *); struct usbd_bus_methods { void (*pipe_init)(struct usbd_device *udev, usb_endpoint_descriptor_t *edesc, struct usbd_pipe *pipe); void (*do_poll)(struct usbd_bus *); usbd_status (*xfer_setup)(struct usbd_device *udev, u_int8_t iface_index, struct usbd_xfer **pxfer, const struct usbd_config *setup_start, const struct usbd_config *setup_end); }; struct usbd_pipe_methods { void (*open)(struct usbd_xfer *xfer); void (*close)(struct usbd_xfer *xfer); void (*enter)(struct usbd_xfer *xfer); void (*start)(struct usbd_xfer *xfer); void (*copy_in)(struct usbd_xfer *xfer); void (*copy_out)(struct usbd_xfer *xfer); }; struct usbd_port { usb_port_status_t status; u_int16_t power; /* mA of current on port */ u_int8_t portno; u_int8_t restartcnt; u_int8_t last_refcount; #define USBD_RESTART_MAX 5 struct usbd_device *device; /* connected device */ struct usbd_device *parent; /* the ports hub */ }; struct usbd_hub { usbd_status (*explore)(struct usbd_device *hub); void *hubsoftc; usb_hub_descriptor_t hubdesc; struct usbd_port ports[0]; }; /*****/ #define USB_PAGE_SIZE PAGE_SIZE struct usbd_page { void *buffer; bus_size_t physaddr; #ifdef __FreeBSD__ bus_dma_tag_t tag; bus_dmamap_t map; #endif #ifdef __NetBSD__ bus_dma_tag_t tag; bus_dmamap_t map; bus_dma_segment_t seg; int32_t seg_count; #endif u_int32_t length; }; struct usbd_page_search { void *buffer; bus_size_t physaddr; u_int32_t length; }; struct usbd_page_cache { struct usbd_page * page_start; struct usbd_page * page_end; struct usbd_page * page_cur; u_int32_t page_offset_buf; u_int32_t page_offset_cur; }; struct usbd_bus { /* filled by HC driver */ device_t bdev; /* base device, host adapter */ struct usbd_bus_methods *methods; bus_dma_tag_t dma_tag; /* filled by USB driver */ struct usbd_port root_port; /* dummy port for root hub */ struct usbd_device * devices[USB_MAX_DEVICES]; u_int8_t is_exploring; u_int8_t wait_explore; u_int8_t needs_explore;/* a hub signalled a change * this variable is protected by * "usb_global_lock" */ u_int8_t use_polling; u_int8_t usbrev; /* USB revision */ #define USBREV_UNKNOWN 0 #define USBREV_PRE_1_0 1 #define USBREV_1_0 2 #define USBREV_1_1 3 #define USBREV_2_0 4 #define USBREV_STR { "unknown", "pre 1.0", "1.0", "1.1", "2.0" } struct usb_device_stats stats; struct mtx mtx; struct proc * event_thread; u_int32_t no_intrs; }; struct usbd_interface { usb_interface_descriptor_t *idesc; u_int8_t alt_index; }; #define usbd_clear_endpoint_toggle(pipe) { \ (pipe)->clearstall = 0; (pipe)->toggle_next = 0; } struct usbd_pipe { usb_endpoint_descriptor_t *edesc; LIST_HEAD(, usbd_xfer) list_head; u_int16_t isoc_next; u_int8_t toggle_next; u_int8_t refcount; u_int8_t clearstall; u_int8_t iface_index; /* default pipe does not use ``iface_index'' */ /* filled by HC driver */ struct usbd_pipe_methods *methods; }; struct usbd_device { struct usbd_bus *bus; /* our controller */ struct usbd_pipe default_pipe; /* pipe 0 */ usb_endpoint_descriptor_t default_ep_desc; /* for pipe 0 */ u_int8_t address; /* device addess */ u_int8_t config; /* current configuration # */ u_int8_t depth; /* distance from root hub */ u_int8_t speed; /* low/full/high speed */ u_int8_t self_powered; /* flag for self powered */ u_int16_t power; /* mA the device uses */ int16_t langid; /* language for strings */ #define USBD_NOLANG (-1) usb_event_cookie_t cookie; /* unique connection id */ struct usbd_port * powersrc; /* upstream hub port, or 0 */ struct usbd_port * myhsport; /* closest high speed port */ struct usbd_device * myhub; /* upstream hub */ usb_device_descriptor_t ddesc; /* device descriptor */ usb_config_descriptor_t *cdesc; /* full config descr */ const struct usbd_quirks *quirks; /* device quirks, always set */ struct usbd_hub * hub; /* only if this is a hub */ device_t subdevs[USB_MAX_ENDPOINTS]; /* array of all sub-devices */ device_t subdevs_end[0]; struct usbd_interface ifaces[USB_MAX_ENDPOINTS]; /* array of all interfaces */ struct usbd_interface ifaces_end[0]; struct usbd_pipe pipes[USB_MAX_ENDPOINTS]; /* array of all pipes */ struct usbd_pipe pipes_end[0]; u_int8_t ifaces_no_probe[(USB_MAX_ENDPOINTS + 7) / 8]; #define USBD_SET_IFACE_NO_PROBE(udev, ii) \ { (udev)->ifaces_no_probe[(ii) >> 3] |= (1 << ((ii) & 7)); } #define USBD_CLR_IFACE_NO_PROBE(udev, ii) \ { (udev)->ifaces_no_probe[(ii) >> 3] &= ~(1 << ((ii) & 7)); } #define USBD_GET_IFACE_NO_PROBE(udev, ii) \ ((udev)->ifaces_no_probe[(ii) >> 3] & (1 << ((ii) & 7))) u_int8_t probed; /* probe state */ #define USBD_PROBED_NOTHING 0 /* default value */ #define USBD_PROBED_SPECIFIC_AND_FOUND 1 #define USBD_PROBED_IFACE_AND_FOUND 2 #define USBD_PROBED_GENERIC_AND_FOUND 3 u_int8_t serial[32]; }; struct usbd_config { u_int8_t type; /* pipe type */ u_int8_t endpoint; /* pipe number */ u_int8_t direction; /* pipe direction */ u_int8_t interval; /* interrupt interval in milliseconds; * used by interrupt pipes */ #define USBD_DEFAULT_INTERVAL 0 u_int16_t timeout; /* milliseconds */ u_int16_t frames; /* number of frames * used in isochronous * mode */ u_int8_t sub_frames; /* number of sub-frames * contained within each * frame. Used in High-Speed * isochronous mode. */ u_int8_t index; /* pipe index to use, if more * than one descriptor matches * type, address, direction ... */ u_int32_t flags; /* flags */ #define USBD_SYNCHRONOUS 0x0001 /* wait for completion */ #define USBD_FORCE_SHORT_XFER 0x0002 /* force a short packet last */ #if (USBD_SHORT_XFER_OK != 0x0004) #define USBD_SHORT_XFER_OK 0x0004 /* allow short reads * NOTE: existing software * expects USBD_SHORT_XFER_OK * to have a value of 0x4. This * flag is also exported by usb.h */ #endif #define USBD_CUSTOM_CLEARSTALL 0x0008 /* used to disable automatic clear-stall * when a device reset request is needed * in addition to the clear stall request */ #define USBD_DEV_OPEN 0x0010 #define USBD_DEV_RECURSED_1 0x0020 #define USBD_DEV_RECURSED_2 0x0040 #define USBD_DEV_TRANSFERRING 0x0080 #define USBD_BANDWIDTH_RECLAIMED 0x0100 #define USBD_USE_POLLING 0x0200 /* used to make synchronous transfers * use polling instead of sleep/wakeup */ #define USBD_UNUSED_3 0x0400 #define USBD_USE_DMA 0x0800 #define USBD_UNUSED_4 0x1000 #define USBD_UNUSED_5 0x2000 #define USBD_UNUSED_6 0x4000 #define USBD_UNUSED_7 0x8000 u_int32_t bufsize; /* total pipe buffer size in bytes */ usbd_callback_t callback; }; #define USBD_TRANSFER_IN_PROGRESS(xfer) \ ((xfer)->flags & USBD_DEV_TRANSFERRING) struct usbd_xfer { struct usbd_pipe * pipe; struct usbd_device * udev; void * buffer; void * priv_sc; void * priv_fifo; struct mtx * priv_mtx; struct usbd_xfer * clearstall_xfer; u_int32_t length; /* bytes */ u_int32_t actlen; /* bytes */ u_int32_t flags; u_int32_t timeout; /* milliseconds */ #define USBD_NO_TIMEOUT 0 #define USBD_DEFAULT_TIMEOUT 5000 /* 5000 ms = 5 seconds */ usbd_status error; usbd_callback_t callback; /* for isochronous transfers */ u_int16_t * frlengths; u_int16_t * frlengths_old; u_int32_t nframes; /* * used by HC driver */ void * usb_sc; struct mtx * usb_mtx; struct usbd_memory_info *usb_root; struct thread * usb_thread; u_int32_t usb_refcount; /* pipe_list is used to start next transfer */ LIST_ENTRY(usbd_xfer) pipe_list; /* interrupt_list is used to check * for finished transfers */ LIST_ENTRY(usbd_xfer) interrupt_list; struct __callout timeout_handle; u_int8_t address; u_int8_t endpoint; u_int8_t interval; /* milliseconds */ u_int16_t max_packet_size; u_int16_t max_frame_size; u_int16_t qh_pos; struct usbd_page_cache buf_data; /* buffer page cache */ struct usbd_page_cache buf_fixup; /* fixup buffer */ void *qh_start; void *td_start; void *td_transfer_first; void *td_transfer_last; void *td_transfer_cache; }; struct usbd_memory_info { void * memory_base; u_int32_t memory_size; u_int32_t memory_refcount; u_int32_t setup_refcount; struct mtx * priv_mtx; struct mtx * usb_mtx; struct usbd_page * page_base; u_int32_t page_size; }; struct usbd_callback_info { struct usbd_xfer *xfer; u_int32_t refcount; }; struct usbd_mbuf { u_int8_t *cur_data_ptr; u_int8_t *min_data_ptr; struct usbd_mbuf *usbd_nextpkt; struct usbd_mbuf *usbd_next; u_int32_t cur_data_len; u_int32_t max_data_len; }; struct usbd_ifqueue { struct usbd_mbuf *ifq_head; struct usbd_mbuf *ifq_tail; int32_t ifq_len; int32_t ifq_maxlen; }; #define USBD_IF_ENQUEUE(ifq, m) do { \ (m)->usbd_nextpkt = NULL; \ if ((ifq)->ifq_tail == NULL) \ (ifq)->ifq_head = (m); \ else \ (ifq)->ifq_tail->usbd_nextpkt = (m); \ (ifq)->ifq_tail = (m); \ (ifq)->ifq_len++; \ } while (0) #define USBD_IF_DEQUEUE(ifq, m) do { \ (m) = (ifq)->ifq_head; \ if (m) { \ if (((ifq)->ifq_head = (m)->usbd_nextpkt) == NULL) { \ (ifq)->ifq_tail = NULL; \ } \ (m)->usbd_nextpkt = NULL; \ (ifq)->ifq_len--; \ } \ } while (0) #define USBD_IF_PREPEND(ifq, m) do { \ (m)->usbd_nextpkt = (ifq)->ifq_head; \ if ((ifq)->ifq_tail == NULL) { \ (ifq)->ifq_tail = (m); \ } \ (ifq)->ifq_head = (m); \ (ifq)->ifq_len++; \ } while (0) #define USBD_IF_QFULL(ifq) ((ifq)->ifq_len >= (ifq)->ifq_maxlen) #define USBD_IF_QLEN(ifq) ((ifq)->ifq_len) #define USBD_IF_POLL(ifq, m) ((m) = (ifq)->ifq_head) #define USBD_MBUF_RESET(m) do { \ (m)->cur_data_ptr = (m)->min_data_ptr; \ (m)->cur_data_len = (m)->max_data_len; \ } while (0) /*---------------------------------------------------------------------------* * structures used by the usbd config thread system *---------------------------------------------------------------------------*/ struct usbd_config_td_softc; struct usbd_config_td_cc; typedef void (usbd_config_td_command_t) (struct usbd_config_td_softc *sc, struct usbd_config_td_cc *cc, u_int16_t reference); typedef void (usbd_config_td_config_copy_t) (struct usbd_config_td_softc *sc, struct usbd_config_td_cc *cc, u_int16_t reference); typedef void (usbd_config_td_end_of_commands_t) (struct usbd_config_td_softc *sc); struct usbd_config_td { struct usbd_ifqueue cmd_free; struct usbd_ifqueue cmd_used; struct proc * config_thread; struct mtx * p_mtx; void * p_softc; void * p_cmd_queue; usbd_config_td_config_copy_t *p_config_copy; usbd_config_td_end_of_commands_t *p_end_of_commands; u_int8_t wakeup_config_td; u_int8_t wakeup_config_td_gone; u_int8_t flag_config_td_sleep; u_int8_t flag_config_td_gone; }; struct usbd_config_td_item { usbd_config_td_command_t *command_func; u_int16_t command_ref; } __attribute__((__aligned__(USB_HOST_ALIGN))); /*---------------------------------------------------------------------------* * structures used by probe and attach *---------------------------------------------------------------------------*/ struct usb_devno { u_int16_t ud_vendor; u_int16_t ud_product; } __packed; #define usb_lookup(tbl, vendor, product) usb_match_device \ ((const struct usb_devno *)(tbl), (sizeof (tbl) / sizeof ((tbl)[0])), \ sizeof ((tbl)[0]), (vendor), (product)) \ /**/ #define USB_PRODUCT_ANY 0xffff struct usb_attach_arg { int port; int configno; int iface_index; int vendor; int product; int release; int matchlvl; struct usbd_device *device; /* current device */ struct usbd_interface *iface; /* current interface */ int usegeneric; struct usbd_interface *ifaces_start; /* all interfaces */ struct usbd_interface *ifaces_end; /* exclusive */ }; /* return values for device_probe() method: */ #define UMATCH_VENDOR_PRODUCT_REV (-10) #define UMATCH_VENDOR_PRODUCT (-20) #define UMATCH_VENDOR_DEVCLASS_DEVPROTO (-30) #define UMATCH_DEVCLASS_DEVSUBCLASS_DEVPROTO (-40) #define UMATCH_DEVCLASS_DEVSUBCLASS (-50) #define UMATCH_VENDOR_PRODUCT_REV_CONF_IFACE (-60) #define UMATCH_VENDOR_PRODUCT_CONF_IFACE (-70) #define UMATCH_VENDOR_IFACESUBCLASS_IFACEPROTO (-80) #define UMATCH_VENDOR_IFACESUBCLASS (-90) #define UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO (-100) #define UMATCH_IFACECLASS_IFACESUBCLASS (-110) #define UMATCH_IFACECLASS (-120) #define UMATCH_IFACECLASS_GENERIC (-130) #define UMATCH_GENERIC (-140) #define UMATCH_NONE (ENXIO) /*---------------------------------------------------------------------------* * prototypes *---------------------------------------------------------------------------*/ /* routines from usb_subr.c */ void usbd_devinfo(struct usbd_device *udev, int showclass, char *dst_ptr, u_int16_t dst_len); const char * usbd_errstr(usbd_status err); void usb_delay_ms(struct usbd_bus *bus, u_int ms); void usbd_delay_ms(struct usbd_device *udev, u_int ms); usb_descriptor_t * usbd_desc_foreach(usb_config_descriptor_t *cd, usb_descriptor_t *desc); struct usb_hid_descriptor; struct usb_hid_descriptor * usbd_get_hdesc(usb_config_descriptor_t *cd, usb_interface_descriptor_t *id); usb_interface_descriptor_t * usbd_find_idesc(usb_config_descriptor_t *cd, u_int16_t iface_index, u_int16_t alt_index); usb_endpoint_descriptor_t * usbd_find_edesc(usb_config_descriptor_t *cd, u_int16_t iface_index, u_int16_t alt_index, u_int16_t endptidx); usb_descriptor_t * usbd_find_descriptor(usb_config_descriptor_t *cd, int type, int subtype); #define USBD_SUBTYPE_ANY (-1) int usbd_get_no_alts(usb_config_descriptor_t *cd, u_int8_t ifaceno); usbd_status usbd_search_and_set_config(struct usbd_device *udev, int no, int msg); usbd_status usbd_set_config_index(struct usbd_device *udev, int index, int msg); int usbd_fill_deviceinfo(struct usbd_device *udev, struct usb_device_info *di, int usedev); usbd_status usbd_fill_iface_data(struct usbd_device *udev, int iface_index, int alt_index); usbd_status usbd_probe_and_attach(device_t parent, int port, struct usbd_port *up); usbd_status usbd_new_device(device_t parent, struct usbd_bus *bus, int depth, int speed, int port, struct usbd_port *up); void usbd_free_device(struct usbd_port *up, u_int8_t free_subdev); void usb_detach_wait(device_t dv); void usb_detach_wakeup(device_t dv); struct usbd_interface * usbd_get_iface(struct usbd_device *udev, u_int8_t iface_index); void usbd_set_desc(device_t dev, struct usbd_device *udev); void * usbd_alloc_mbufs(struct malloc_type *type, struct usbd_ifqueue *ifq, u_int32_t block_size, u_int16_t block_number); void usbd_get_page(struct usbd_page_cache *cache, u_int32_t offset, struct usbd_page_search *res); void usbd_copy_in(struct usbd_page_cache *cache, u_int32_t offset, const void *ptr, u_int32_t len); void usbd_m_copy_in(struct usbd_page_cache *cache, u_int32_t dst_offset, struct mbuf *m, u_int32_t src_offset, u_int32_t src_len); void usbd_copy_out(struct usbd_page_cache *cache, u_int32_t offset, void *ptr, u_int32_t len); void usbd_bzero(struct usbd_page_cache *cache, u_int32_t offset, u_int32_t len); u_int8_t usbd_page_alloc(bus_dma_tag_t tag, struct usbd_page *page, u_int32_t npages); void usbd_page_free(struct usbd_page *page, u_int32_t npages); void * usbd_page_get_buf(struct usbd_page *page, u_int32_t size); bus_size_t usbd_page_get_phy(struct usbd_page *page, u_int32_t size); void usbd_page_set_start(struct usbd_page_cache *pc, struct usbd_page *page_ptr, u_int32_t size); void usbd_page_set_end(struct usbd_page_cache *pc, struct usbd_page *page_ptr, u_int32_t size); u_int32_t usbd_page_fit_obj(struct usbd_page *page, u_int32_t size, u_int32_t obj_len); void * usbd_mem_alloc(bus_dma_tag_t parent, u_int32_t size, u_int8_t align_power); bus_size_t usbd_mem_vtophys(void *ptr, u_int32_t size); void usbd_mem_free(void *ptr, u_int32_t size); bus_dma_tag_t usbd_dma_tag_alloc(bus_dma_tag_t parent, u_int32_t size, u_int32_t alignment); void usbd_dma_tag_free(bus_dma_tag_t tag); void * usbd_mem_alloc_sub(bus_dma_tag_t tag, struct usbd_page *page, u_int32_t size, u_int32_t alignment); void usbd_mem_free_sub(struct usbd_page *page); #ifdef __FreeBSD__ #if (__FreeBSD_version >= 700020) #define device_get_dma_tag(dev) bus_get_dma_tag(dev) #else #define device_get_dma_tag(dev) NULL /* XXX */ #endif #endif void usbd_std_transfer_setup(struct usbd_xfer *xfer, const struct usbd_config *setup, u_int16_t max_packet_size, u_int16_t max_frame_size); u_int8_t usbd_make_str_desc(void *ptr, u_int16_t max_len, const char *s); u_int32_t mtx_drop_recurse(struct mtx *mtx); void mtx_pickup_recurse(struct mtx *mtx, u_int32_t recurse_level); u_int8_t usbd_config_td_setup(struct usbd_config_td *ctd, void *priv_sc, struct mtx *priv_mtx, usbd_config_td_config_copy_t *p_func_cc, usbd_config_td_end_of_commands_t *p_func_eoc, u_int16_t item_size, u_int16_t item_count); void usbd_config_td_stop(struct usbd_config_td *ctd); void usbd_config_td_unsetup(struct usbd_config_td *ctd); void usbd_config_td_queue_command(struct usbd_config_td *ctd, usbd_config_td_command_t *command_func, u_int16_t command_ref); u_int8_t usbd_config_td_is_gone(struct usbd_config_td *ctd); u_int8_t usbd_config_td_sleep(struct usbd_config_td *ctd, u_int32_t timeout); struct mbuf * usbd_ether_get_mbuf(void); /* routines from usb.c */ #if 0 extern struct mtx usb_global_lock; #else /* XXX currently only the Giant lock can sleep */ #define usb_global_lock Giant #endif void usbd_add_dev_event(int type, struct usbd_device *udev); void usbd_add_drv_event(int type, struct usbd_device *udev, device_t dev); void usb_needs_explore(struct usbd_device *udev); extern u_int8_t usb_driver_added_refcount; void usb_needs_probe_and_attach(void); /* routines from usb_transfer.c */ #ifdef USB_DEBUG void usbd_dump_iface(struct usbd_interface *iface); void usbd_dump_device(struct usbd_device *udev); void usbd_dump_queue(struct usbd_pipe *pipe); void usbd_dump_pipe(struct usbd_pipe *pipe); void usbd_dump_xfer(struct usbd_xfer *xfer); #endif u_int32_t usb_get_devid(device_t dev); struct usbd_pipe * usbd_get_pipe(struct usbd_device *udev, u_int8_t iface_index, const struct usbd_config *setup); usbd_status usbd_interface_count(struct usbd_device *udev, u_int8_t *count); usbd_status usbd_transfer_setup(struct usbd_device *udev, u_int8_t iface_index, struct usbd_xfer **pxfer, const struct usbd_config *setup_start, u_int16_t n_setup, void *priv_sc, struct mtx *priv_mtx); void usbd_transfer_unsetup(struct usbd_xfer **pxfer, u_int16_t n_setup); void usbd_std_isoc_copy_in(struct usbd_xfer *xfer); void usbd_std_isoc_copy_out(struct usbd_xfer *xfer); void usbd_std_bulk_intr_copy_in(struct usbd_xfer *xfer); void usbd_std_bulk_intr_copy_out(struct usbd_xfer *xfer); void usbd_std_ctrl_copy_in(struct usbd_xfer *xfer); void usbd_std_ctrl_copy_out(struct usbd_xfer *xfer); void usbd_start_hardware(struct usbd_xfer *xfer); void usbd_transfer_start_safe(struct usbd_xfer *xfer); void usbd_transfer_start(struct usbd_xfer *xfer); void usbd_transfer_stop(struct usbd_xfer *xfer); void __usbd_callback(struct usbd_xfer *xfer); void usbd_do_callback(struct usbd_callback_info *ptr, struct usbd_callback_info *limit); void usbd_transfer_done(struct usbd_xfer *xfer, usbd_status error); void usbd_transfer_enqueue(struct usbd_xfer *xfer); void usbd_transfer_dequeue(struct usbd_xfer *xfer); void usbd_default_callback(struct usbd_xfer *xfer); usbd_status usbd_do_request(struct usbd_device *udev, usb_device_request_t *req, void *data); usbd_status usbd_do_request_flags(struct usbd_device *udev, usb_device_request_t *req, void *data, u_int32_t flags, int *actlen, u_int32_t timeout); usbd_status usbd_do_request_mtx(struct usbd_device *udev, struct mtx *mtx, usb_device_request_t *req, void *data); usbd_status usbd_do_request_flags_mtx(struct usbd_device *udev, struct mtx *mtx, usb_device_request_t *req, void *data, u_int32_t flags, int *actlen, u_int32_t timeout); void usbd_fill_get_report(usb_device_request_t *req, u_int8_t iface_no, u_int8_t type, u_int8_t id, u_int16_t size); void usbd_fill_set_report(usb_device_request_t *req, u_int8_t iface_no, u_int8_t type, u_int8_t id, u_int16_t size); void usbd_clear_stall_tr_setup(struct usbd_xfer *xfer1, struct usbd_xfer *xfer2); void usbd_clear_stall_tr_transferred(struct usbd_xfer *xfer1, struct usbd_xfer *xfer2); void usbd_clearstall_callback(struct usbd_xfer *xfer); void usbd_do_poll(struct usbd_device *udev); void usbd_set_polling(struct usbd_device *udev, int on); int usbd_ratecheck(struct timeval *last); const struct usb_devno * usb_match_device(const struct usb_devno *tbl, u_int nentries, u_int size, u_int16_t vendor, u_int16_t product); int usbd_driver_load(struct module *mod, int what, void *arg); /* routines from usb_requests.c */ usbd_status usbreq_reset_port(struct usbd_device *udev, int port, usb_port_status_t *ps); usbd_status usbreq_get_desc(struct usbd_device *udev, int type, int index, int len, void *desc, int timeout); usbd_status usbreq_get_string_any(struct usbd_device *udev, int si, char *buf, int len); usbd_status usbreq_get_string_desc(struct usbd_device *udev, int sindex, int langid, usb_string_descriptor_t *sdesc, int *plen); usbd_status usbreq_get_config_desc(struct usbd_device *udev, int confidx, usb_config_descriptor_t *d); usbd_status usbreq_get_config_desc_full(struct usbd_device *udev, int conf, void *d, int size); usbd_status usbreq_get_device_desc(struct usbd_device *udev, usb_device_descriptor_t *d); usbd_status usbreq_get_interface(struct usbd_device *udev, u_int8_t iface_index, u_int8_t *aiface); usbd_status usbreq_set_interface(struct usbd_device *udev, u_int8_t iface_index, u_int8_t altno); usbd_status usbreq_get_device_status(struct usbd_device *udev, usb_status_t *st); usbd_status usbreq_get_hub_descriptor(struct usbd_device *udev, usb_hub_descriptor_t *hd); usbd_status usbreq_get_hub_status(struct usbd_device *udev, usb_hub_status_t *st); usbd_status usbreq_set_address(struct usbd_device *udev, int addr); usbd_status usbreq_get_port_status(struct usbd_device *udev, int port, usb_port_status_t *ps); usbd_status usbreq_clear_hub_feature(struct usbd_device *udev, int sel); usbd_status usbreq_set_hub_feature(struct usbd_device *udev, int sel); usbd_status usbreq_clear_port_feature(struct usbd_device *udev, int port, int sel); usbd_status usbreq_set_port_feature(struct usbd_device *udev, int port, int sel); usbd_status usbreq_set_protocol(struct usbd_device *udev, u_int8_t iface_index, u_int16_t report); usbd_status usbreq_set_report(struct usbd_device *udev, u_int8_t iface_index, u_int8_t type, u_int8_t id, void *data, int len); usbd_status usbreq_get_report(struct usbd_device *udev, u_int8_t iface_index, u_int8_t type, u_int8_t id, void *data, int len); usbd_status usbreq_set_idle(struct usbd_device *udev, u_int8_t iface_index, int duration, int id); usbd_status usbreq_get_report_descriptor(struct usbd_device *udev, int ifcno, int size, void *d); usbd_status usbreq_read_report_desc(struct usbd_device *udev, u_int8_t iface_index, void **descp, int *sizep, usb_malloc_type mem); usbd_status usbreq_set_config(struct usbd_device *udev, int conf); usbd_status usbreq_get_config(struct usbd_device *udev, u_int8_t *conf); /**/ #define usbd_get_device_descriptor(udev) (&(udev)->ddesc) #define usbd_get_config_descriptor(udev) ((udev)->cdesc) #define usbd_get_interface_descriptor(iface) ((iface)->idesc) #define usbd_get_interface_altindex(iface) ((iface)->alt_index) #define usbd_get_quirks(udev) ((udev)->quirks) #define usbd_get_speed(udev) ((udev)->speed) #define usbd_get_hid_descriptor usbd_get_hdesc #define usbd_set_config_no usbd_search_and_set_config /* helper for computing offsets */ #define POINTER_TO_UNSIGNED(ptr) \ (((u_int8_t *)(ptr)) - ((u_int8_t *)0)) /* routines from "usb_cdev.c" */ struct usb_cdev; struct cdev; struct mtx; extern int32_t usb_cdev_sleep(struct usb_cdev *sc, int32_t fflags, u_int32_t timeout); extern void usb_cdev_wakeup(struct usb_cdev *sc); extern void usb_cdev_unlock(struct usb_cdev *sc, int32_t fflags); extern int32_t usb_cdev_lock(struct usb_cdev *sc, int32_t fflags, int32_t error); extern int32_t usb_cdev_attach(struct usb_cdev *sc, void *priv_sc, struct mtx *priv_mtx, const char **pp_dev, uid_t _uid, gid_t _gid, int _perms, u_int32_t rd_size, u_int16_t rd_packets, u_int32_t wr_size, u_int16_t wr_packets); extern void usb_cdev_detach(struct usb_cdev *sc); extern void usb_cdev_put_data(struct usb_cdev *sc, u_int8_t *buf, u_int32_t len, u_int8_t what); extern void usb_cdev_put_data_error(struct usb_cdev *sc); extern u_int8_t usb_cdev_get_data(struct usb_cdev *sc, u_int8_t *buf, u_int32_t len, u_int32_t *actlen, u_int8_t what); extern void usb_cdev_get_data_error(struct usb_cdev *sc); typedef int32_t (usb_cdev_open_t)(struct usb_cdev *sc, int32_t fflags, int32_t mode, struct thread *td); typedef int32_t (usb_cdev_ioctl_t)(struct usb_cdev *sc, u_long cmd, caddr_t addr, int32_t fflags, struct thread *td); typedef void (usb_cdev_cmd_t)(struct usb_cdev *sc); struct usb_cdev { struct usbd_ifqueue sc_rdq_free; struct usbd_ifqueue sc_rdq_used; struct usbd_ifqueue sc_wrq_free; struct usbd_ifqueue sc_wrq_used; struct selinfo sc_read_sel; struct selinfo sc_write_sel; /* various pointers */ void * sc_rdq_pointer; void * sc_wrq_pointer; struct mtx * sc_mtx_ptr; void * sc_priv_ptr; #define USB_CDEV_COUNT 4 struct cdev * sc_cdev[USB_CDEV_COUNT]; struct cdev * sc_last_cdev; struct proc * sc_async_rd; /* process that wants SIGIO */ struct proc * sc_async_wr; /* process that wants SIGIO */ /* multiplexer functions */ usb_cdev_open_t * sc_open; usb_cdev_ioctl_t * sc_ioctl; usb_cdev_cmd_t * sc_start_read; usb_cdev_cmd_t * sc_stop_read; usb_cdev_cmd_t * sc_start_write; usb_cdev_cmd_t * sc_stop_write; u_int32_t sc_cur_context; u_int32_t sc_flags; /* synchronization flags */ #define USB_CDEV_FLAG_GONE 0x00000001 #define USB_CDEV_FLAG_FLUSHING_WRITE 0x00000002 #define USB_CDEV_FLAG_OPEN_READ 0x00000004 #define USB_CDEV_FLAG_OPEN_WRITE 0x00000008 #define USB_CDEV_FLAG_SLEEP_READ 0x00000010 #define USB_CDEV_FLAG_SLEEP_WRITE 0x00000020 #define USB_CDEV_FLAG_SLEEP_IOCTL_RD 0x00000040 #define USB_CDEV_FLAG_SLEEP_IOCTL_WR 0x00000080 #define USB_CDEV_FLAG_WAKEUP_READ 0x00000100 #define USB_CDEV_FLAG_WAKEUP_WRITE 0x00000200 #define USB_CDEV_FLAG_WAKEUP_IOCTL_RD 0x00000400 #define USB_CDEV_FLAG_WAKEUP_IOCTL_WR 0x00000800 #define USB_CDEV_FLAG_SELECT_READ 0x00001000 #define USB_CDEV_FLAG_SELECT_WRITE 0x00002000 #define USB_CDEV_FLAG_CLOSING_READ 0x00004000 #define USB_CDEV_FLAG_CLOSING_WRITE 0x00008000 #define USB_CDEV_FLAG_ERROR_READ 0x00010000 /* can be set to indicate error */ #define USB_CDEV_FLAG_ERROR_WRITE 0x00020000 /* can be set to indicate error */ /* other flags */ #define USB_CDEV_FLAG_FWD_SHORT 0x00040000 /* can be set to forward short transfers */ #define USB_CDEV_FLAG_READ_ONLY 0x00080000 /* device is read only */ #define USB_CDEV_FLAG_WRITE_ONLY 0x00100000 /* device is read only */ #define USB_CDEV_FLAG_WAKEUP_RD_IMMED 0x00200000 /* wakeup read thread immediately */ #define USB_CDEV_FLAG_WAKEUP_WR_IMMED 0x00400000 /* wakeup write thread immediately */ u_int8_t sc_wakeup_read; /* dummy */ u_int8_t sc_wakeup_write; /* dummy */ u_int8_t sc_wakeup_flush; /* dummy */ u_int8_t sc_wakeup_close_read; /* dummy */ u_int8_t sc_wakeup_close_write; /* dummy */ u_int8_t sc_wakeup_detach; /* dummy */ u_int8_t sc_wakeup_ioctl; /* dummy */ u_int8_t sc_wakeup_ioctl_rdwr; /* dummy */ u_int8_t sc_first_open; /* set when first device * is being opened */ }; #endif /* _USB_SUBR_H_ */ pwcbsd/usb/usb_transfer.c000644 000423 000000 00000077433 10551670754 016260 0ustar00luigiwheel000000 000000 /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include __FBSDID("$FreeBSD: src/sys/dev/usb2/usb_transfer.c $"); #ifdef USB_DEBUG void usbd_dump_iface(struct usbd_interface *iface) { printf("usbd_dump_iface: iface=%p\n", iface); if(iface == NULL) { return; } printf(" iface=%p idesc=%p altindex=%d\n", iface, iface->idesc, iface->alt_index); return; } void usbd_dump_device(struct usbd_device *udev) { printf("usbd_dump_device: dev=%p\n", udev); if(udev == NULL) { return; } printf(" bus=%p \n" " address=%d config=%d depth=%d speed=%d self_powered=%d\n" " power=%d langid=%d\n", udev->bus, udev->address, udev->config, udev->depth, udev->speed, udev->self_powered, udev->power, udev->langid); return; } void usbd_dump_queue(struct usbd_pipe *pipe) { struct usbd_xfer *xfer; printf("usbd_dump_queue: pipe=%p\n", pipe); LIST_FOREACH(xfer, &pipe->list_head, pipe_list) { printf(" xfer=%p\n", xfer); } return; } void usbd_dump_pipe(struct usbd_pipe *pipe) { if(pipe) { printf("usbd_dump_pipe: pipe=%p", pipe); printf(" edesc=%p isoc_next=%d toggle_next=%d", pipe->edesc, pipe->isoc_next, pipe->toggle_next); if(pipe->edesc) { printf(" bEndpointAddress=0x%02x", pipe->edesc->bEndpointAddress); } printf("\n"); usbd_dump_queue(pipe); } else { printf("usbd_dump_pipe: pipe=NULL\n"); } return; } void usbd_dump_xfer(struct usbd_xfer *xfer) { printf("usbd_dump_xfer: xfer=%p\n", xfer); if(xfer == NULL) { return; } if(xfer->pipe == NULL) { printf("xfer %p: pipe=NULL\n", xfer); return; } printf("xfer %p: udev=%p vid=0x%04x pid=0x%04x addr=%d " "pipe=%p ep=0x%02x attr=0x%02x\n", xfer, xfer->udev, UGETW(xfer->udev->ddesc.idVendor), UGETW(xfer->udev->ddesc.idProduct), xfer->udev->address, xfer->pipe, xfer->pipe->edesc->bEndpointAddress, xfer->pipe->edesc->bmAttributes); return; } #endif u_int32_t usb_get_devid(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); return ((uaa->vendor << 16) | (uaa->product)); } struct usbd_pipe * usbd_get_pipe(struct usbd_device *udev, u_int8_t iface_index, const struct usbd_config *setup) { struct usbd_pipe *pipe; uint8_t index = setup->index; uint8_t ea; uint8_t at; PRINTFN(8,("udev=%p iface_index=%d address=0x%x " "type=0x%x dir=0x%x index=%d\n", udev, iface_index, setup->endpoint, setup->type, setup->direction, setup->index)); /* NOTE: pipes should be searched from the beginning */ for (pipe = udev->pipes; ((pipe >= udev->pipes) && (pipe < udev->pipes_end)); pipe++) { if ((pipe->edesc == NULL) || (pipe->iface_index != iface_index)) { continue; } ea = pipe->edesc->bEndpointAddress; at = pipe->edesc->bmAttributes; if (((setup->direction == (ea & (UE_DIR_IN|UE_DIR_OUT))) || (setup->direction == UE_DIR_ANY)) && ((setup->endpoint == (ea & UE_ADDR)) || (setup->endpoint == UE_ADDR_ANY)) && ((setup->type == (at & UE_XFERTYPE)) || (setup->type == UE_TYPE_ANY) || ((setup->type == UE_BULK_INTR) && (at & 2)))) { if(!index--) { goto found; } } } /* Match against default pipe last, so that "any pipe", * "any address" and "any direction" returns the first * pipe of the interface. "iface_index" and "direction" * is ignored: */ if((setup->endpoint == 0) && (setup->type == 0)) { pipe = &udev->default_pipe; goto found; } return NULL; found: return pipe; } usbd_status usbd_interface_count(struct usbd_device *udev, u_int8_t *count) { if(udev->cdesc == NULL) { return (USBD_NOT_CONFIGURED); } *count = udev->cdesc->bNumInterface; return (USBD_NORMAL_COMPLETION); } /*---------------------------------------------------------------------------* * usbd_transfer_setup - setup an array of USB transfers * * NOTE: must always call unsetup after setup * NOTE: the parameter "iface_index" is ignored in * case of control pipes * * The idea is that the USB device driver should pre-allocate all * its transfers by one call to this function. *---------------------------------------------------------------------------*/ usbd_status usbd_transfer_setup(struct usbd_device *udev, u_int8_t iface_index, struct usbd_xfer **pxfer, const struct usbd_config *setup_start, u_int16_t n_setup, void *priv_sc, struct mtx *priv_mtx) { const struct usbd_config *setup_end = setup_start + n_setup; const struct usbd_config *setup; struct usbd_memory_info *info; struct usbd_xfer *xfer; usbd_status error = 0; u_int16_t n; WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "usbd_transfer_setup can sleep!"); /* do some checking first */ if(n_setup == 0) { PRINTFN(5, ("setup array has zero length!\n")); return USBD_INVAL; } if(priv_mtx == NULL) { PRINTFN(5, ("using global lock\n")); priv_mtx = &usb_global_lock; } for(setup = setup_start, n = n_setup; n--; setup++) { if(setup->bufsize == 0xffffffff) { error = USBD_BAD_BUFSIZE; PRINTF(("invalid bufsize\n")); } if(setup->flags & USBD_SYNCHRONOUS) { if(setup->callback != &usbd_default_callback) { error = USBD_SYNC_TRANSFER_MUST_USE_DEFAULT_CALLBACK; PRINTF(("synchronous transfers " "must use default callback\n")); } } if(setup->flags & (~(USBD_SYNCHRONOUS| USBD_FORCE_SHORT_XFER| USBD_SHORT_XFER_OK| USBD_CUSTOM_CLEARSTALL| USBD_USE_POLLING| USBD_USE_DMA))) { error = USBD_BAD_FLAG; PRINTF(("invalid flag(s) specified: " "0x%08x\n", setup->flags)); } if(setup->callback == NULL) { error = USBD_NO_CALLBACK; PRINTF(("no callback\n")); } pxfer[n] = NULL; } if(error) { goto done; } error = (udev->bus->methods->xfer_setup) (udev,iface_index,pxfer,setup_start,setup_end); /* common setup */ for(setup = setup_start, n = n_setup; n--; setup++) { xfer = pxfer[n]; if(xfer) { xfer->priv_sc = priv_sc; xfer->priv_mtx = priv_mtx; xfer->udev = udev; if(xfer->pipe) { xfer->pipe->refcount++; } info = xfer->usb_root; info->memory_refcount++; info->setup_refcount++; } } done: if(error) { usbd_transfer_unsetup(pxfer, n_setup); } return (error); } /*---------------------------------------------------------------------------* * usbd_drop_refcount * * This function is called from various places, and its job is to * wakeup "usbd_transfer_unsetup", when is safe to free the memory. *---------------------------------------------------------------------------*/ static void usbd_drop_refcount(struct usbd_memory_info *info) { mtx_lock(info->usb_mtx); __KASSERT(info->memory_refcount != 0, ("Invalid memory " "reference count!\n")); if ((--(info->memory_refcount)) == 0) { wakeup(info); } mtx_unlock(info->usb_mtx); return; } /*---------------------------------------------------------------------------* * usbd_transfer_unsetup - unsetup/free an array of USB transfers * * NOTE: if the transfer was in progress, the callback will * called with "xfer->error=USBD_CANCELLED", before this * function returns *---------------------------------------------------------------------------*/ void usbd_transfer_unsetup(struct usbd_xfer **pxfer, u_int16_t n_setup) { struct usbd_xfer *xfer; struct usbd_memory_info *info; int error; WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "usbd_transfer_unsetup can sleep!"); while(n_setup--) { xfer = pxfer[n_setup]; pxfer[n_setup] = NULL; if(xfer) { if(xfer->pipe) { mtx_lock(xfer->priv_mtx); usbd_transfer_stop(xfer); /* NOTE: default pipe does not * have an interface, even if * pipe->iface_index == 0 */ xfer->pipe->refcount--; mtx_unlock(xfer->priv_mtx); } __callout_drain(&(xfer->timeout_handle)); if(xfer->usb_root) { info = xfer->usb_root; mtx_lock(info->usb_mtx); __KASSERT(info->memory_refcount != 0, ("Invalid memory " "reference count!\n")); __KASSERT(info->setup_refcount != 0, ("Invalid setup " "reference count!\n")); info->memory_refcount--; info->setup_refcount--; if (info->setup_refcount == 0) { while (info->memory_refcount > 0) { error = msleep(info, info->usb_mtx, 0, "usb_mem_wait", 0); } mtx_unlock(info->usb_mtx); /* free DMA'able memory, if any */ usbd_page_free(info->page_base, info->page_size); /* free the "memory_base" last, hence the * "info" structure is contained within * the "memory_base"! */ free(info->memory_base, M_USB); } else { mtx_unlock(info->usb_mtx); } } } } return; } void usbd_std_isoc_copy_in(struct usbd_xfer *xfer) { u_int32_t x; u_int32_t length; if (UE_GET_DIR(xfer->endpoint) == UE_DIR_OUT) { length = 0; for (x = 0; x < xfer->nframes; x++) { length += xfer->frlengths[x]; } /* only one copy is needed, hence * the frames are back to back: */ usbd_copy_in(&(xfer->buf_data), 0, xfer->buffer, length); } else { for (x = 0; x < xfer->nframes; x++) { xfer->frlengths_old[x] = xfer->frlengths[x]; } } return; } void usbd_std_isoc_copy_out(struct usbd_xfer *xfer) { u_int8_t *ptr; u_int32_t x; u_int32_t offset; if (UE_GET_DIR(xfer->endpoint) == UE_DIR_IN) { ptr = xfer->buffer; offset = 0; for (x = 0; x < xfer->nframes; x++) { usbd_copy_out(&(xfer->buf_data), offset, ptr, xfer->frlengths[x]); ptr += xfer->frlengths_old[x]; offset += xfer->frlengths_old[x]; } } return; } void usbd_std_bulk_intr_copy_in(struct usbd_xfer *xfer) { if (UE_GET_DIR(xfer->endpoint) == UE_DIR_OUT) { usbd_copy_in(&(xfer->buf_data), 0, xfer->buffer, xfer->length); } return; } void usbd_std_bulk_intr_copy_out(struct usbd_xfer *xfer) { if (UE_GET_DIR(xfer->endpoint) == UE_DIR_IN) { usbd_copy_out(&(xfer->buf_data), 0, xfer->buffer, xfer->actlen); } return; } void usbd_std_ctrl_copy_in(struct usbd_xfer *xfer) { u_int32_t len = xfer->length; usb_device_request_t *req = xfer->buffer; if ((len >= sizeof(*req)) && (req->bmRequestType & UT_READ)) { len = sizeof(*req); } usbd_copy_in(&(xfer->buf_data), 0, xfer->buffer, len); return; } void usbd_std_ctrl_copy_out(struct usbd_xfer *xfer) { u_int32_t len = xfer->actlen; usb_device_request_t *req = xfer->buffer; if ((len >= sizeof(*req)) && (req->bmRequestType & UT_READ)) { usbd_copy_out(&(xfer->buf_data), sizeof(*req), (req+1), len - sizeof(*req)); } return; } /* CALLBACK EXAMPLES: * ================== * * USBD_CHECK_STATUS() overview of possible program paths: * ======================================================= * * +->-----------------------+ * | | * +-<-+-------[tr_setup]--------+-<-+-<-[start/restart] * | | * | | * | | * +------>-[tr_transferred]---------+ * | | * +--------->-[tr_error]------------+ * * NOTE: the USB-driver automatically * recovers from errors if * "xfer->clearstall_xfer" is set * * Host-transmit callback example (bulk/interrupt/isochronous): * ============================================================ * static void * usb_callback_tx(struct usbd_xfer *xfer) * { * USBD_CHECK_STATUS(xfer); * * tr_transferred: * tr_setup: * * ... setup "xfer->length" ... * * ... write data to buffer ... * * tr_error: * * ... [re-]transfer "xfer->buffer" ... * * usbd_start_hardware(xfer); * return; * } * * Host-receive callback example (bulk/interrupt/isochronous): * =========================================================== * static void * usb_callback_rx(struct usbd_xfer *xfer) * { * USBD_CHECK_STATUS(xfer); * * tr_transferred: * * ... process data in buffer ... * * tr_setup: * * ... setup "xfer->length" ... * * tr_error: * * ... [re-]transfer "xfer->buffer" ... * * usbd_start_hardware(xfer); * return; * } * * * "usbd_start_hardware()" is called when * "xfer->buffer" is ready for transfer * * "usbd_start_hardware()" should only be called * from callback */ /*---------------------------------------------------------------------------* * usbd_start_hardware - start USB hardware for the given transfer *---------------------------------------------------------------------------*/ void usbd_start_hardware(struct usbd_xfer *xfer) { PRINTFN(0,("xfer=%p, pipe=%p len=%d dir=%s\n", xfer, xfer->pipe, xfer->length, ((xfer->pipe->edesc->bEndpointAddress & (UE_DIR_IN|UE_DIR_OUT)) == UE_DIR_IN) ? "in" : "out")); #ifdef USB_DEBUG if(usbdebug > 0) { mtx_lock(xfer->usb_mtx); usbd_dump_pipe(xfer->pipe); mtx_unlock(xfer->usb_mtx); } #endif mtx_assert(xfer->priv_mtx, MA_OWNED); if(xfer->flags & USBD_DEV_OPEN) { /* set USBD_DEV_TRANSFERRING and USBD_DEV_RECURSED_2 */ xfer->flags |= (USBD_DEV_TRANSFERRING|USBD_DEV_RECURSED_2); if(xfer->pipe->clearstall && xfer->clearstall_xfer) { #ifdef USB_DEBUG if(xfer->clearstall_xfer->flags & USBD_DEV_TRANSFERRING) { PRINTF(("clearstall_xfer is transferrring!\n")); } #endif /* the polling flag is inherited */ if(xfer->flags & USBD_USE_POLLING) xfer->clearstall_xfer->flags |= USBD_USE_POLLING; else xfer->clearstall_xfer->flags &= ~USBD_USE_POLLING; /* store pointer to transfer */ xfer->clearstall_xfer->priv_sc = xfer; usbd_transfer_start(xfer->clearstall_xfer); } else { if(!(xfer->flags & USBD_USE_DMA)) { /* copy in data */ (xfer->pipe->methods->copy_in)(xfer); } mtx_lock(xfer->usb_mtx); /* enter the transfer */ (xfer->pipe->methods->enter)(xfer); xfer->usb_thread = (xfer->flags & USBD_USE_POLLING) ? curthread : NULL; mtx_unlock(xfer->usb_mtx); } } return; } void usbd_transfer_start_safe(struct usbd_xfer *xfer) { mtx_lock(xfer->priv_mtx); usbd_transfer_start(xfer); mtx_unlock(xfer->priv_mtx); return; } /*---------------------------------------------------------------------------* * usbd_transfer_start - start a USB transfer * * NOTE: this function can be called any number of times * NOTE: if USBD_SYNCHRONOUS is set in "xfer->flags", then this * function will sleep for transfer completion * NOTE: if USBD_USE_POLLING is set in "xfer->flags", then this * function will spin until transfer is completed *---------------------------------------------------------------------------*/ void usbd_transfer_start(struct usbd_xfer *xfer) { mtx_assert(xfer->priv_mtx, MA_OWNED); if(!(xfer->flags & USBD_DEV_OPEN)) { xfer->flags |= USBD_DEV_OPEN; /* * open transfer */ mtx_lock(xfer->usb_mtx); (xfer->pipe->methods->open)(xfer); mtx_unlock(xfer->usb_mtx); } /* "USBD_DEV_TRANSFERRING" is only changed * when "priv_mtx" is locked; * check first recurse flag */ if(!(xfer->flags & (USBD_DEV_TRANSFERRING))) { /* call callback */ __usbd_callback(xfer); /* wait for completion * if the transfer is synchronous */ if(xfer->flags & (USBD_SYNCHRONOUS|USBD_USE_POLLING)) { u_int timeout = xfer->timeout +1; struct usbd_bus *bus = xfer->udev->bus; while(xfer->flags & USBD_DEV_TRANSFERRING) { if(bus->use_polling || (xfer->flags & USBD_USE_POLLING)) { if(!timeout--) { /* stop the transfer */ usbd_transfer_stop(xfer); break; } /* delay one millisecond */ DELAY(1000); /* call the interrupt handler, * which will call __usbd_callback(): */ (bus->methods->do_poll)(bus); } else { u_int32_t level; level = mtx_drop_recurse(xfer->priv_mtx); if(msleep(xfer, xfer->priv_mtx, (PRIBIO|PCATCH), "usbsync", 0)) { /* stop the transfer */ usbd_transfer_stop(xfer); } mtx_pickup_recurse(xfer->priv_mtx, level); break; } } } } return; } /*---------------------------------------------------------------------------* * usbd_transfer_stop - stop a USB transfer * * NOTE: this function can be called any number of times *---------------------------------------------------------------------------*/ void usbd_transfer_stop(struct usbd_xfer *xfer) { mtx_assert(xfer->priv_mtx, MA_OWNED); if(xfer->flags & USBD_DEV_OPEN) { xfer->flags &= ~USBD_DEV_OPEN; /* * stop clearstall first */ if(xfer->clearstall_xfer) { usbd_transfer_stop(xfer->clearstall_xfer); } /* * close transfer (should not call callback) */ mtx_lock(xfer->usb_mtx); (xfer->pipe->methods->close)(xfer); /* always set error */ xfer->error = USBD_CANCELLED; if(xfer->flags & USBD_DEV_TRANSFERRING) { /* increment refcount so that scheduled * callbacks, if any, are not called by * the interrupt or timeout routines: */ xfer->usb_refcount++; /* call callback, which * will clear USBD_DEV_TRANSFERRING */ __usbd_callback(xfer); } mtx_unlock(xfer->usb_mtx); } return; } /*---------------------------------------------------------------------------* * __usbd_callback * * This is a wrapper for USB callbacks, which handles * recursation, which can happen during boot. *---------------------------------------------------------------------------*/ void __usbd_callback(struct usbd_xfer *xfer) { mtx_assert(xfer->priv_mtx, MA_OWNED); /* check first recurse flag */ if(!(xfer->flags & USBD_DEV_RECURSED_1)) { do { /* set both recurse flags */ xfer->flags |= (USBD_DEV_RECURSED_2| USBD_DEV_RECURSED_1); /* check if any data must be copied out */ if(xfer->flags & USBD_DEV_TRANSFERRING) { if(!(xfer->flags & USBD_USE_DMA)) { /* copy out data */ (xfer->pipe->methods->copy_out)(xfer); } } /* call processing routine */ (xfer->callback)(xfer); /* check second recurse flag */ } while(!(xfer->flags & USBD_DEV_RECURSED_2)); /* clear first recurse flag */ xfer->flags &= ~USBD_DEV_RECURSED_1; } else { /* clear second recurse flag */ xfer->flags &= ~USBD_DEV_RECURSED_2; } return; } /*---------------------------------------------------------------------------* * usbd_do_callback * * This function is used to call back a list of USB callbacks. *---------------------------------------------------------------------------*/ void usbd_do_callback(struct usbd_callback_info *ptr, struct usbd_callback_info *limit) { struct usbd_xfer *xfer; if(limit < ptr) { /* parameter order switched */ register void *temp = ptr; ptr = limit; limit = temp; } while(ptr < limit) { xfer = ptr->xfer; /* * During the unlocked period, the * transfer can be restarted by * another thread, which must be * checked here: */ mtx_lock(xfer->priv_mtx); if(xfer->usb_refcount == ptr->refcount) { /* call callback */ __usbd_callback(xfer); } /* * else already called back ! */ mtx_unlock(xfer->priv_mtx); usbd_drop_refcount(xfer->usb_root); ptr++; } return; } /*---------------------------------------------------------------------------* * usbd_transfer_done * * NOTE: this function does not call the callback! *---------------------------------------------------------------------------*/ void usbd_transfer_done(struct usbd_xfer *xfer, usbd_status error) { mtx_assert(xfer->usb_mtx, MA_OWNED); PRINTFN(5,("xfer=%p pipe=%p status=%d " "actlen=%d\n", xfer, xfer->pipe, error, xfer->actlen)); #if defined(DIAGNOSTIC) || defined(USB_DEBUG) if(xfer->pipe == NULL) { printf("xfer=%p, pipe=NULL\n", xfer); return; } #endif /* count completed transfers */ ++(xfer->udev->bus->stats.uds_requests [xfer->pipe->edesc->bmAttributes & UE_XFERTYPE]); /* check for short transfers */ if(!error) { if(xfer->actlen > xfer->length) { printf("%s: overrun actlen(%d) > len(%d)\n", __FUNCTION__, xfer->actlen, xfer->length); xfer->actlen = xfer->length; } if((xfer->actlen < xfer->length) && !(xfer->flags & USBD_SHORT_XFER_OK)) { printf("%s: short transfer actlen(%d) < len(%d)\n", __FUNCTION__, xfer->actlen, xfer->length); error = USBD_SHORT_XFER; } } xfer->error = error; return; } /*---------------------------------------------------------------------------* * usbd_transfer_enqueue * * This function is used to put a USB transfer on * the pipe list. If there was no previous * USB transfer on the list, the start method of * the transfer will be called. *---------------------------------------------------------------------------*/ void usbd_transfer_enqueue(struct usbd_xfer *xfer) { mtx_assert(xfer->usb_mtx, MA_OWNED); /* if xfer is not inserted, * insert xfer in xfer queue */ if(xfer->pipe_list.le_prev == NULL) { LIST_INSERT_HEAD(&xfer->pipe->list_head, xfer, pipe_list); /* start first transfer enqueued */ if(xfer->pipe_list.le_next == NULL) { (xfer->pipe->methods->start)(xfer); } } return; } /*---------------------------------------------------------------------------* * usbd_transfer_dequeue * * This function is used to remove a USB transfer from * the pipe list. If the first USB transfer on the pipe * list is removed, the start method of the next USB * transfer will be called, if any. *---------------------------------------------------------------------------*/ void usbd_transfer_dequeue(struct usbd_xfer *xfer) { mtx_assert(xfer->usb_mtx, MA_OWNED); /* if two transfers are queued, the * second transfer must be started * before the first is called back */ /* if xfer is not removed, * remove xfer from xfer queue */ if(xfer->pipe_list.le_prev) { LIST_REMOVE(xfer,pipe_list); /* if started transfer is dequeued, * start next transfer */ if((xfer->pipe_list.le_next == 0) && /* last xfer */ (!LIST_EMPTY(&xfer->pipe->list_head))) { (xfer->pipe->methods->start) ((void *) (((u_int8_t *)(xfer->pipe_list.le_prev)) - POINTER_TO_UNSIGNED(&LIST_NEXT((struct usbd_xfer *)0,pipe_list)))); } xfer->pipe_list.le_prev = 0; } return; } void usbd_default_callback(struct usbd_xfer *xfer) { USBD_CHECK_STATUS(xfer); tr_setup: /**/ usbd_start_hardware(xfer); return; tr_transferred: tr_error: if((xfer->flags & USBD_SYNCHRONOUS) && (!(xfer->udev->bus->use_polling || (xfer->flags & USBD_USE_POLLING)))) { wakeup(xfer); } return; } /*---------------------------------------------------------------------------* * usbd_do_request_flags / usbd_do_request * * NOTE: the caller should hold "Giant" while calling this function *---------------------------------------------------------------------------*/ usbd_status usbd_do_request(struct usbd_device *udev, usb_device_request_t *req, void *data) { return usbd_do_request_flags_mtx (udev, NULL, req, data, 0, NULL, USBD_DEFAULT_TIMEOUT); } usbd_status usbd_do_request_flags(struct usbd_device *udev, usb_device_request_t *req, void *data, u_int32_t flags, int *actlen, u_int32_t timeout) { return usbd_do_request_flags_mtx (udev, NULL, req, data, flags, actlen, timeout); } /*---------------------------------------------------------------------------* * usbd_do_request_flags_mtx / usbd_do_request_mtx * * NOTE: the caller should hold "mtx" while calling this function *---------------------------------------------------------------------------*/ usbd_status usbd_do_request_mtx(struct usbd_device *udev, struct mtx *mtx, usb_device_request_t *req, void *data) { return usbd_do_request_flags_mtx (udev, mtx, req, data, 0, 0, USBD_DEFAULT_TIMEOUT); } usbd_status usbd_do_request_flags_mtx(struct usbd_device *udev, struct mtx *mtx, usb_device_request_t *req, void *data, u_int32_t flags, int *actlen, u_int32_t timeout) { struct usbd_config usbd_config[1] = { /* zero */ }; struct usbd_xfer *xfer = NULL; u_int16_t length = UGETW(req->wLength); u_int32_t level = 0; usbd_status err; PRINTFN(4,("udev=%p bmRequestType=0x%02x bRequest=0x%02x " "wValue=0x%02x%02x wIndex=0x%02x%02x wLength=0x%02x%02x\n", udev, req->bmRequestType, req->bRequest, req->wValue[1], req->wValue[0], req->wIndex[1], req->wIndex[0], req->wLength[1], req->wLength[0])); usbd_config[0].type = UE_CONTROL; usbd_config[0].endpoint = 0; /* control pipe */ usbd_config[0].direction = -1; usbd_config[0].timeout = timeout; usbd_config[0].flags = (flags|USBD_SYNCHRONOUS|USBD_USE_DMA); usbd_config[0].bufsize = sizeof(*req) + length; usbd_config[0].callback = &usbd_default_callback; if (mtx) { level = mtx_drop_recurse(mtx); mtx_unlock(mtx); } /* setup transfer */ err = usbd_transfer_setup(udev, 0, &xfer, &usbd_config[0], 1, NULL, mtx); if (mtx) { mtx_lock(mtx); mtx_pickup_recurse(mtx, level); } if(err) { goto done; } usbd_copy_in(&(xfer->buf_data), 0, req, sizeof(*req)); if(!(req->bmRequestType & UT_READ)) { usbd_copy_in(&(xfer->buf_data), sizeof(*req), data, length); } usbd_transfer_start_safe(xfer); if(req->bmRequestType & UT_READ) { usbd_copy_out(&(xfer->buf_data), sizeof(*req), data, length); } err = xfer->error; if(actlen != NULL) { actlen[0] = (xfer->actlen < sizeof(req[0])) ? 0 : (xfer->actlen - sizeof(req[0])); } done: if (mtx) { level = mtx_drop_recurse(mtx); mtx_unlock(mtx); } usbd_transfer_unsetup(&xfer, 1); if (mtx) { mtx_lock(mtx); mtx_pickup_recurse(mtx, level); } return (err); } void usbd_fill_get_report(usb_device_request_t *req, u_int8_t iface_no, u_int8_t type, u_int8_t id, u_int16_t size) { req->bmRequestType = UT_READ_CLASS_INTERFACE; req->bRequest = UR_GET_REPORT; USETW2(req->wValue, type, id); USETW(req->wIndex, iface_no); USETW(req->wLength, size); return; } void usbd_fill_set_report(usb_device_request_t *req, u_int8_t iface_no, u_int8_t type, u_int8_t id, u_int16_t size) { req->bmRequestType = UT_WRITE_CLASS_INTERFACE; req->bRequest = UR_SET_REPORT; USETW2(req->wValue, type, id); USETW(req->wIndex, iface_no); USETW(req->wLength, size); return; } void usbd_clear_stall_tr_setup(struct usbd_xfer *xfer1, struct usbd_xfer *xfer2) { usb_device_request_t req; mtx_assert(xfer1->priv_mtx, MA_OWNED); mtx_assert(xfer2->priv_mtx, MA_OWNED); /* setup a clear-stall packet */ req.bmRequestType = UT_WRITE_ENDPOINT; req.bRequest = UR_CLEAR_FEATURE; USETW(req.wValue, UF_ENDPOINT_HALT); req.wIndex[0] = xfer2->pipe->edesc->bEndpointAddress; req.wIndex[1] = 0; USETW(req.wLength, 0); /* double check the length */ if (xfer1->length != sizeof(req)) { printf("%s:%d: invalid transfer length, %d bytes!\n", __FUNCTION__, __LINE__, xfer1->length); return; } /* copy in the transfer */ if (xfer1->flags & USBD_USE_DMA) { usbd_copy_in(&(xfer1->buf_data), 0, &req, sizeof(req)); } else { bcopy(&req, xfer1->buffer, sizeof(req)); } usbd_start_hardware(xfer1); return; } void usbd_clear_stall_tr_transferred(struct usbd_xfer *xfer1, struct usbd_xfer *xfer2) { mtx_assert(xfer1->priv_mtx, MA_OWNED); mtx_assert(xfer2->priv_mtx, MA_OWNED); mtx_lock(xfer2->usb_mtx); /* * clear any stall and make sure * that DATA0 toggle will be * used next: */ xfer2->pipe->clearstall = 0; xfer2->pipe->toggle_next = 0; mtx_unlock(xfer2->usb_mtx); return; } void usbd_clearstall_callback(struct usbd_xfer *xfer) { USBD_CHECK_STATUS(xfer); tr_setup: usbd_clear_stall_tr_setup(xfer, xfer->priv_sc); return; tr_transferred: tr_error: PRINTFN(3,("xfer=%p\n", xfer)); /* NOTE: some devices reject this command, * so ignore a STALL */ usbd_clear_stall_tr_transferred(xfer, xfer->priv_sc); usbd_start_hardware(xfer->priv_sc); return; } /* clearstall config: * * .type = UE_CONTROL, * .endpoint = 0, * .direction = -1, * .timeout = USBD_DEFAULT_TIMEOUT, * .flags = 0, * .bufsize = sizeof(usb_device_request_t), * .callback = &usbd_clearstall_callback, */ /* * called from keyboard driver when in polling mode */ void usbd_do_poll(struct usbd_device *udev) { (udev->bus->methods->do_poll)(udev->bus); return; } void usbd_set_polling(struct usbd_device *udev, int on) { if(on) { udev->bus->use_polling++; } else { udev->bus->use_polling--; } /* make sure there is nothing pending to do */ if(udev->bus->use_polling) { (udev->bus->methods->do_poll)(udev->bus); } return; } /* * usbd_ratecheck() can limit the number of error messages that occurs. * When a device is unplugged it may take up to 0.25s for the hub driver * to notice it. If the driver continuosly tries to do I/O operations * this can generate a large number of messages. */ int usbd_ratecheck(struct timeval *last) { if(last->tv_sec == time_second) { return (0); } last->tv_sec = time_second; return (1); } /* * Search for a vendor/product pair in an array. The item size is * given as an argument. */ const struct usb_devno * usb_match_device(const struct usb_devno *tbl, u_int nentries, u_int size, u_int16_t vendor, u_int16_t product) { while(nentries-- > 0) { if((tbl->ud_vendor == vendor) && ((tbl->ud_product == product) || (tbl->ud_product == USB_PRODUCT_ANY))) { return (tbl); } tbl = (const struct usb_devno *) (((const u_int8_t *)tbl) + size); } return (NULL); } int usbd_driver_load(struct module *mod, int what, void *arg) { /* XXX should implement something like a * function that removes all generic devices */ return (0); } pwcbsd/usb/usbdevs000644 000423 000000 00000165252 10551670754 015012 0ustar00luigiwheel000000 000000 $FreeBSD: src/sys/dev/usb/usbdevs,v 1.258 2006/04/27 17:33:25 imp Exp $ /* $NetBSD: usbdevs,v 1.392 2004/12/29 08:38:44 imp Exp $ */ /*- * Copyright (c) 1998-2004 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * List of known USB vendors * * USB.org publishes a VID list of USB-IF member companies at * http://www.usb.org/developers/tools * Note that it does not show companies that have obtained a Vendor ID * without becoming full members. * * Please note that these IDs do not do anything. Adding an ID here and * regenerating the usbdevs.h and usbdevs_data.h only makes a symbolic name * available to the source code and does not change any functionality, nor * does it make your device available to a specific driver. * It will however make the descriptive string available if a device does not * provide the string itself. * * After adding a vendor ID VNDR and a product ID PRDCT you will have the * following extra defines: * #define USB_VENDOR_VNDR 0x???? * #define USB_PRODUCT_VNDR_PRDCT 0x???? * * You may have to add these defines to the respective probe routines to * make the device recognised by the appropriate device driver. */ vendor EGALAX2 0x0123 eGalax vendor LTS 0x0386 LTS vendor AOX 0x03e8 AOX vendor THESYS 0x03e9 Thesys vendor DATABROADCAST 0x03ea Data Broadcasting vendor ATMEL 0x03eb Atmel vendor IWATSU 0x03ec Iwatsu America vendor MITEL 0x03ee Mitel vendor MITSUMI 0x03ee Mitsumi vendor HP 0x03f0 Hewlett Packard vendor GENOA 0x03f1 Genoa vendor OAK 0x03f2 Oak vendor ADAPTEC 0x03f3 Adaptec vendor DIEBOLD 0x03f4 Diebold vendor SIEMENSELECTRO 0x03f5 Siemens Electromechanical vendor EPSONIMAGING 0x03f8 Epson Imaging vendor KEYTRONIC 0x03f9 KeyTronic vendor OPTI 0x03fb OPTi vendor ELITEGROUP 0x03fc Elitegroup vendor XILINX 0x03fd Xilinx vendor FARALLON 0x03fe Farallon Communications vendor NATIONAL 0x0400 National Semiconductor vendor NATIONALREG 0x0401 National Registry vendor ACERLABS 0x0402 Acer Labs vendor FTDI 0x0403 Future Technology Devices vendor NCR 0x0404 NCR vendor SYNOPSYS2 0x0405 Synopsys vendor FUJITSUICL 0x0406 Fujitsu-ICL vendor FUJITSU2 0x0407 Fujitsu Personal Systems vendor QUANTA 0x0408 Quanta vendor NEC 0x0409 NEC vendor KODAK 0x040a Eastman Kodak vendor WELTREND 0x040b Weltrend vendor VIA 0x040d VIA vendor MCCI 0x040e MCCI vendor MELCO 0x0411 Melco vendor WINBOND 0x0416 Winbond vendor PHOENIX 0x041a Phoenix vendor CREATIVE 0x041e Creative vendor NOKIA 0x0421 Nokia vendor ADI 0x0422 ADI vendor CATC 0x0423 Computer Access Technology vendor SMC2 0x0424 SMC vendor MOTOROLA_HK 0x0425 Motorola HK vendor GRAVIS 0x0428 Advanced Gravis Computer Tech. vendor CIRRUSLOGIC 0x0429 Cirrus Logic vendor INNOVATIVE 0x042c Innovative Semiconductors vendor MOLEX 0x042f Molex vendor SUN2 0x0430 Sun Microsystems (unofficial) vendor UNISYS 0x0432 Unisys vendor TAUGA 0x0436 Taugagreining HF vendor AMD 0x0438 ADM vendor LEXMARK 0x043d Lexmark vendor LG 0x043e LG Electronics vendor NANAO 0x0440 NANAO vendor GATEWAY 0x0443 Gateway 2000 vendor NMB 0x0446 NMB vendor ALPS 0x044e Alps vendor THRUST 0x044f Thrustmaster vendor TI 0x0451 Texas Instruments vendor ANALOGDEVICES 0x0456 Analog Devices vendor SIS 0x0457 SIS vendor KYE 0x0458 KYE vendor DIAMOND2 0x045a Diamond (Supra) vendor RENESAS 0x045b Renesas vendor MICROSOFT 0x045e Microsoft vendor PRIMAX 0x0461 Primax vendor MGE 0x0463 MGE vendor AMP 0x0464 AMP vendor CHERRY 0x046a Cherry vendor MEGATRENDS 0x046b American Megatrends vendor LOGITECH 0x046d Logitech vendor BTC 0x046e BTC vendor PHILIPS 0x0471 Philips vendor SUN 0x0472 Sun Microsystems (offical) vendor SANYO 0x0474 Sanyo vendor SEAGATE 0x0477 Seagate vendor CONNECTIX 0x0478 Connectix vendor SEMTECH 0x047a Semtech vendor KENSINGTON 0x047d Kensington vendor LUCENT 0x047e Lucent vendor PLANTRONICS 0x047f Plantronics vendor KYOCERA 0x0482 Kyocera vendor STMICRO 0x0483 STMicroelectronics vendor FOXCONN 0x0489 Foxconn vendor YAMAHA 0x0499 YAMAHA vendor COMPAQ 0x049f Compaq vendor HITACHI 0x04a4 Hitachi vendor ACERP 0x04a5 Acer Peripherals vendor VISIONEER 0x04a7 Visioneer vendor CANON 0x04a9 Canon vendor NIKON 0x04b0 Nikon vendor PAN 0x04b1 Pan International vendor IBM 0x04b3 IBM vendor CYPRESS 0x04b4 Cypress vendor ROHM 0x04b5 ROHM vendor COMPAL 0x04b7 Compal vendor EPSON 0x04b8 Seiko Epson vendor RAINBOW 0x04b9 Rainbow vendor IODATA 0x04bb I-O Data vendor TDK 0x04bf TDK vendor 3COMUSR 0x04c1 U.S. Robotics vendor METHODE 0x04c2 Methode vendor MAXISWITCH 0x04c3 Maxi Switch vendor LOCKHEEDMER 0x04c4 Lockheed Martin Energy Research vendor FUJITSU 0x04c5 Fujitsu vendor TOSHIBAAM 0x04c6 Toshiba vendor MICROMACRO 0x04c7 Micro Macro vendor KONICA 0x04c8 Konica vendor LITEON 0x04ca Lite-On vendor FUJIPHOTO 0x04cb Fuji Photo vendor PHILIPSSEMI 0x04cc Philips vendor TATUNG 0x04cd Tatung vendor SCANLOGIC 0x04ce ScanLogic vendor MYSON 0x04cf Myson vendor DIGI2 0x04d0 Digi vendor ITTCANON 0x04d1 ITT Canon vendor ALTEC 0x04d2 Altec Lansing vendor LSI 0x04d4 LSI vendor MENTORGRAPHICS 0x04d6 Mentor Graphics vendor HOLTEK 0x04d9 Holtek vendor PANASONIC 0x04da Panasonic (Matsushita) vendor HUANHSIN 0x04dc Huan Hsin vendor SHARP 0x04dd Sharp vendor IIYAMA 0x04e1 Iiyama vendor SHUTTLE 0x04e6 Shuttle vendor ELO 0x04e7 Elo TouchSystems vendor SAMSUNG 0x04e8 Samsung vendor NORTHSTAR 0x04eb Northstar vendor TOKYOELECTRON 0x04ec Tokyo Electron vendor ANNABOOKS 0x04ed Annabooks vendor JVC 0x04f1 JVC vendor CHICONY 0x04f2 Chicony vendor ELAN 0x04f3 Elan vendor NEWNEX 0x04f7 Newnex vendor BROTHER 0x04f9 Brother vendor DALLAS 0x04fa Dallas Semiconductor vendor SUNPLUS 0x04fc Sunplus vendor PFU 0x04fe PFU vendor FUJIKURA 0x0501 Fujikura/DDK vendor ACER 0x0502 Acer vendor 3COM 0x0506 3Com vendor HOSIDEN 0x0507 Hosiden vendor AZTECH 0x0509 Aztech vendor BELKIN 0x050d Belkin vendor KAWATSU 0x050f Kawatsu vendor FCI 0x0514 FCI vendor LONGWELL 0x0516 Longwell vendor COMPOSITE 0x0518 Composite vendor STAR 0x0519 Star Micronics vendor APC 0x051d American Power Conversion vendor SCIATLANTA 0x051e Scientific Atlanta vendor TSM 0x0520 TSM vendor CONNECTEK 0x0522 Connectek vendor NETCHIP 0x0525 NetChip vendor ALTRA 0x0527 ALTRA vendor ATI 0x0528 ATI vendor AKS 0x0529 AKS vendor TEKOM 0x052b Tekom vendor CANONDEV 0x052c Canon vendor WACOMTECH 0x0531 Wacom vendor INVENTEC 0x0537 Inventec vendor SHYHSHIUN 0x0539 Shyh Shiun Terminals vendor PREHWERKE 0x053a Preh Werke Gmbh & Co. KG vendor SYNOPSYS 0x053f Synopsys vendor UNIACCESS 0x0540 Universal Access vendor VIEWSONIC 0x0543 ViewSonic vendor XIRLINK 0x0545 Xirlink vendor ANCHOR 0x0547 Anchor vendor SONY 0x054c Sony vendor FUJIXEROX 0x0550 Fuji Xerox vendor VISION 0x0553 VLSI Vision vendor ASAHIKASEI 0x0556 Asahi Kasei vendor ATEN 0x0557 ATEN vendor MUSTEK 0x055f Mustek vendor TELEX 0x0562 Telex vendor CHINON 0x0564 Chinon vendor PERACOM 0x0565 Peracom Networks vendor ALCOR2 0x0566 Alcor Micro vendor XYRATEX 0x0567 Xyratex vendor WACOM 0x056a WACOM vendor ETEK 0x056c e-TEK vendor EIZO 0x056d EIZO vendor ELECOM 0x056e Elecom vendor CONEXANT 0x0572 Conexant vendor HAUPPAUGE 0x0573 Hauppauge vendor BAFO 0x0576 BAFO vendor YEDATA 0x057b Y-E Data vendor AVM 0x057c AVM vendor QUICKSHOT 0x057f Quickshot vendor ROLAND 0x0582 Roland vendor ROCKFIRE 0x0583 Rockfire vendor RATOC 0x0584 RATOC vendor ZYXEL 0x0586 ZyXEL vendor INFINEON 0x058b Infineon vendor MICREL 0x058d Micrel vendor ALCOR 0x058f Alcor vendor OMRON 0x0590 OMRON vendor NIIGATA 0x0598 Niigata vendor IOMEGA 0x059b Iomega vendor ATREND 0x059c A-Trend vendor AID 0x059d AID vendor LACIE 0x059f LaCie vendor FUJIFILM 0x05a2 Fuji Film vendor ARC 0x05a3 ARC vendor ORTEK 0x05a4 Ortek vendor BOSE 0x05a7 Bose vendor OMNIVISION 0x05a9 OmniVision vendor INSYSTEM 0x05ab In-System vendor APPLE 0x05ac Apple Computer vendor YCCABLE 0x05ad Y.C. Cable vendor DIGITALPERSONA 0x05ba DigitalPersona vendor RAFI 0x05bd RAFI vendor TYCO 0x05be Tyco vendor KAWASAKI 0x05c1 Kawasaki vendor DIGI 0x05c5 Digi vendor QUALCOMM 0x05c6 Qualcomm vendor QTRONIX 0x05c7 Qtronix vendor FOXLINK 0x05c8 Foxlink vendor RICOH 0x05ca Ricoh vendor ELSA 0x05cc ELSA vendor SCIWORX 0x05ce sci-worx vendor BRAINBOXES 0x05d1 Brainboxes vendor ULTIMA 0x05d8 Ultima vendor AXIOHM 0x05d9 Axiohm vendor MICROTEK 0x05da Microtek vendor SUNTAC 0x05db SUN Corporation vendor LEXAR 0x05dc Lexar Media vendor DELTA 0x05dd Delta vendor SYMBOL 0x05e0 Symbol vendor SYNTEK 0x05e1 Syntek vendor GENESYS 0x05e3 Genesys vendor FUJI 0x05e5 Fuji vendor KEITHLEY 0x05e6 Keithley vendor EIZONANAO 0x05e7 EIZO Nanao vendor KLSI 0x05e9 Kawasaki LSI vendor FFC 0x05eb FFC vendor ANKO 0x05ef Anko vendor PIENGINEERING 0x05f3 P.I. Engineering vendor AOC 0x05f6 AOC vendor CHIC 0x05fe Chic vendor BARCO 0x0600 Barco vendor BRIDGE 0x0607 Bridge vendor SOLIDYEAR 0x060b Solid Year vendor BIORAD 0x0614 Bio-Rad vendor MACALLY 0x0618 Macally vendor ACTLABS 0x061c Act Labs vendor ALARIS 0x0620 Alaris vendor APEX 0x0624 Apex vendor VIVITAR 0x0636 Vivitar vendor AVISION 0x0638 Avision vendor TEAC 0x0644 TEAC vendor SGI 0x065e Silicon Graphics vendor SANWASUPPLY 0x0663 Sanwa Supply vendor LINKSYS 0x066b Linksys vendor ACERSA 0x066e Acer vendor SIGMATEL 0x066f Sigmatel vendor DRAYTEK 0x0675 DrayTek vendor AIWA 0x0677 Aiwa vendor ACARD 0x0678 ACARD vendor PROLIFIC 0x067b Prolific vendor SIEMENS 0x067c Siemens vendor AVANCELOGIC 0x0680 Avance Logic vendor MINOLTA 0x0686 Minolta vendor CHPRODUCTS 0x068e CH Products vendor HAGIWARA 0x0693 Hagiwara Sys-Com vendor CTX 0x0698 Chuntex vendor ASKEY 0x069a Askey vendor SAITEK 0x06a3 Saitek vendor ALCATELT 0x06b9 Alcatel vendor AGFA 0x06bd AGFA-Gevaert vendor ASIAMD 0x06be Asia Microelectronic Development vendor BIZLINK 0x06c4 Bizlink vendor KEYSPAN 0x06cd Keyspan / InnoSys Inc. vendor AASHIMA 0x06d6 Aashima vendor MULTITECH 0x06e0 MultiTech vendor ADS 0x06e1 ADS vendor ALCATELM 0x06e4 Alcatel vendor SIRIUS 0x06ea Sirius vendor GUILLEMOT 0x06f8 Guillemot vendor BOSTON 0x06fd Boston Acoustics vendor SMC 0x0707 SCM vendor PUTERCOM 0x0708 Putercom vendor MCT 0x0711 MCT vendor IMATION 0x0718 Imation vendor SONYERICSSON 0x0731 Sony Ericsson vendor EICON 0x0734 Eicon Networks vendor DIGITALSTREAM 0x074e Digital Stream vendor AUREAL 0x0755 Aureal vendor MIDIMAN 0x0763 Midiman vendor LINKSYS2 0x077b Linksys vendor GRIFFIN 0x077d Griffin vendor SANDISK 0x0781 SanDisk vendor JENOPTIK 0x0784 Jenoptik vendor LOGITEC 0x0789 Logitec vendor BRIMAX 0x078e Brimax vendor AXIS 0x0792 Axis vendor ABL 0x0794 ABL vendor SAGEM 0x079b Sagem vendor SUNCOMM 0x079c Sun Communications vendor ALFADATA 0x079d Alfadata vendor NATIONALTECH 0x07a2 National Technical vendor ONNTO 0x07a3 Onnto vendor BE 0x07a4 Be vendor ADMTEK 0x07a6 ADMtek vendor COREGA 0x07aa Corega vendor FREECOM 0x07ab Freecom vendor MICROTECH 0x07af Microtech vendor GENERALINSTMNTS 0x07b2 General Instruments (Motorola) vendor OLYMPUS 0x07b4 Olympus vendor ABOCOM 0x07b8 AboCom vendor KEISOKUGIKEN 0x07c1 Keisokugiken vendor ONSPEC 0x07c4 OnSpec vendor APG 0x07c5 APG vendor BUG 0x07c8 BUG vendor ALLIEDTELESYN 0x07c9 Allied Telesyn vendor AVERMEDIA 0x07ca AVerMedia vendor SIIG 0x07cc SIIG vendor CASIO 0x07cf CASIO vendor APTIO 0x07d2 Aptio vendor ARASAN 0x07da Arasan vendor ALLIEDCABLE 0x07e6 Allied Cable vendor STSN 0x07ef STSN vendor ZOOM 0x0803 Zoom vendor PCS 0x0810 Personal Communication Systems vendor BROADLOGIC 0x0827 BroadLogic vendor HANDSPRING 0x082d Handspring vendor PALM 0x0830 Palm vendor SOURCENEXT 0x0833 SOURCENEXT vendor ACTIONSTAR 0x0835 Action Star vendor SAMSUNG_TECHWIN 0x0839 Samsung Techwin vendor ACCTON 0x083a Accton vendor DIAMOND 0x0841 Diamond vendor NETGEAR 0x0846 BayNETGEAR vendor ACTIVEWIRE 0x0854 ActiveWire vendor BBELECTRONICS 0x0856 B&B Electronics vendor PORTGEAR 0x085a PortGear vendor SYSTEMTALKS 0x086e System Talks vendor METRICOM 0x0870 Metricom vendor ADESSOKBTEK 0x087c ADESSO vendor JATON 0x087d Jaton vendor APT 0x0880 APT vendor BOCARESEARCH 0x0885 Boca Research vendor ANDREA 0x08a8 Andrea vendor BURRBROWN 0x08bb Burr-Brown Japan vendor 2WIRE 0x08c8 2Wire vendor AIPTEK 0x08ca AIPTEK vendor SMARTBRIDGES 0x08d1 SmartBridges vendor BILLIONTON 0x08dd Billionton vendor EXTENDED 0x08e9 Extended vendor MSYSTEMS 0x08ec M-Systems vendor AUTHENTEC 0x08ff AuthenTec vendor AUDIOTECHNICA 0x0909 Audio-Technica vendor TRUMPION 0x090a Trumpion vendor ALATION 0x0910 Alation vendor CONCORDCAMERA 0x0919 Concord Camera vendor GOHUBS 0x0921 GoHubs vendor BIOMETRIC 0x0929 American Biometric vendor TOSHIBA 0x0930 Toshiba vendor PLEXTOR 0x093b Plextor vendor INTREPIDCS 0x093c Intrepid vendor YANO 0x094f Yano vendor KINGSTON 0x0951 Kingston vendor BLUEWATER 0x0956 BlueWater vendor AGILENT 0x0957 Agilent vendor PORTSMITH 0x095a Portsmith vendor ACERW 0x0967 Acer vendor ADIRONDACK 0x0976 Adirondack Wire & Cable vendor BECKHOFF 0x0978 Beckhoff vendor MINDSATWORK 0x097a Minds At Work vendor POINTCHIPS 0x09a6 PointChips vendor INTERSIL 0x09aa Intersil vendor ALTIUS 0x09b3 Altius Solutions vendor ARRIS 0x09c1 Arris Interactive vendor ACTIVCARD 0x09c3 ACTIVCARD vendor ACTISYS 0x09c4 ACTiSYS vendor AFOURTECH 0x09da A-FOUR TECH vendor AIMEX 0x09dc AIMEX vendor ADDONICS 0x09df Addonics vendor AKAI 0x09e8 AKAI vendor ARESCOM 0x09f5 ARESCOM vendor BAY 0x09f9 Bay Associates vendor ALTERA 0x09fb Altera vendor CSR 0x0a12 Cambridge Silicon Radio vendor TREK 0x0a16 Trek vendor ASAHIOPTICAL 0x0a17 Asahi Optical vendor BOCASYSTEMS 0x0a43 Boca Systems vendor MEDIAGEAR 0x0a48 MediaGear vendor BROADCOM 0x0a5c Broadcom vendor GREENHOUSE 0x0a6b GREENHOUSE vendor GEOCAST 0x0a79 Geocast vendor ZYDAS 0x0ace ZyDAS Technology vendor NEODIO 0x0aec Neodio vendor VODAFONE 0x0af0 Vodafone vendor ASUS 0x0b05 ASUS vendor TODOS 0x0b0c Todos Data System vendor SIIG2 0x0b39 SIIG vendor TEKRAM 0x0b3b Tekram vendor HAL 0x0b41 HAL vendor EMS 0x0b43 EMS vendor NEC2 0x0b62 NEC vendor ATI2 0x0b6f ATI vendor ZEEVO 0x0b7a Zeevo vendor KURUSUGAWA 0x0b7e Kurusugawa vendor ASIX 0x0b95 ASIX vendor USR 0x0baf U.S. Robotics vendor REALTEK 0x0bda RealTek vendor ADDONICS2 0x0bf6 Addonics vendor AGATE 0x0c08 Agate vendor DMI 0x0c0b DMI vendor CHICONY2 0x0c45 Chicony vendor SEALEVEL 0x0c52 Sealevel vendor LUWEN 0x0c76 Luwen vendor ZCOM 0x0cde Z-Com vendor TANGTOP 0x0d3d Tangtop vendor SMC3 0x0d5c SMC vendor ADDON 0x0d7d Add-on Technology vendor ACDC 0x0d7e ACDC vendor ABC 0x0d8c ABC vendor MSI 0x0db0 Micro Star International vendor SITECOMEU 0x0df6 Sitecom Europe vendor HAWKING 0x0e66 Hawking vendor GMATE 0x0e7e G.Mate, Inc vendor OTI 0x0ea0 Ours vendor PILOTECH 0x0eaf Pilotech vendor EGALAX 0x0eef eGalax vendor MICROTUNE 0x0f4d Microtune vendor VTECH 0x0f88 VTech vendor QUALCOMM2 0x1004 Qualcomm vendor GIGABYTE 0x1044 GIGABYTE vendor WESTERN 0x1058 Western Digital vendor MOTOROLA 0x1063 Motorola vendor CCYU 0x1065 CCYU vendor PLX 0x10b5 PLX vendor ASANTE 0x10bd Asante vendor JRC 0x1145 JRC vendor DELORME 0x1163 DeLorme vendor SERVERWORKS 0x1166 ServerWorks vendor ACERCM 0x1189 Acer Communications & Multimedia vendor TWINMOS 0x126f TwinMOS vendor TSUNAMI 0x1241 Tsunami vendor CREATIVE2 0x1292 Creative Labs vendor BELKIN2 0x1293 Belkin vendor CYBERTAN 0x129b CyberTAN Technology vendor AINCOMM 0x12fd Aincomm vendor MOBILITY 0x1342 Mobility vendor LINKSYS4 0x13b1 Linksys vendor SHARK 0x13d2 Shark vendor WISTRONNEWEB 0x1435 Wistron NeWeb vendor SILICOM 0x1485 Silicom vendor RALINK 0x148f Ralink Technology vendor IMAGINATION 0x149a Imagination Technologies vendor CONCEPTRONIC 0x14b2 Conceptronic vendor PLANEX3 0x14ea Planex Communications vendor SILICONPORTALS 0x1527 Silicon Portals vendor PNY 0x154b PNY vendor UMEDIA 0x157e U-MEDIA Communications vendor SOHOWARE 0x15e8 SOHOware vendor UMAX 0x1606 UMAX vendor INSIDEOUT 0x1608 Inside Out Networks vendor ENTREGA 0x1645 Entrega vendor ACTIONTEC 0x1668 Actiontec vendor LINKSYS3 0x1915 Linksys vendor DLINK 0x2001 D-Link vendor PLANEX2 0x2019 Planex Communications vendor ERICSSON 0x2282 Ericsson vendor MOTOROLA2 0x22b8 Motorola vendor TRIPPLITE 0x2478 Tripp-Lite vendor HIROSE 0x2631 Hirose vendor NHJ 0x2770 NHJ vendor PLANEX 0x2c02 Planex vendor VIDZMEDIA 0x3275 VidzMedia vendor AEI 0x3334 AEI vendor PQI 0x3538 PQI vendor DAISY 0x3579 Daisy vendor NI 0x3923 National Instruments vendor MICRONET 0x3980 Micronet vendor IODATA2 0x40bb I-O Data vendor IRIVER 0x4102 iRiver vendor DELL 0x413c Dell vendor AVERATEC 0x50c2 Averatec vendor SWEEX 0x5173 Sweex vendor ONSPEC2 0x55aa OnSpec vendor ZINWELL 0x5a57 Zinwell vendor SITECOM 0x6189 Sitecom vendor 3COM2 0x6891 3Com vendor INTEL 0x8086 Intel vendor HP2 0xf003 Hewlett Packard /* * List of known products. Grouped by vendor. */ /* 3Com products */ product 3COM HOMECONN 0x009d HomeConnect Camera product 3COM 3CREB96 0x00a0 Bluetooth dongle product 3COM 3C19250 0x03E8 3C19250 Ethernet product 3COM 3CRSHEW696 0x0a01 3CRSHEW696 Wireless adapter product 3COM USR56K 0x3021 U.S.Robotics 56000 Voice Faxmodem Pro product 3COM 3C460 0x11f8 HomeConnect 3C460 product 3COM 3C460B 0x4601 HomeConnect 3C460B product 3COMUSR OFFICECONN 0x0082 3Com OfficeConnect Analog Modem product 3COMUSR USRISDN 0x008f 3Com U.S. Robotics Pro ISDN TA product 3COMUSR HOMECONN 0x009d 3Com HomeConnect camera product 3COMUSR USR56K 0x3021 U.S.Robotics 56000 Voice Faxmodem Pro product 3COM2 3CRUSB10075 0xa727 3CRUSB10075 /* AboCom products */ product ABOCOM XX1 0x110c XX1 product ABOCOM XX2 0x200c XX2 product ABOCOM URE450 0x4000 URE450 Ethernet product ABOCOM UFE1000 0x4002 UFE1000 Fast Ethernet product ABOCOM DSB650TX_PNA 0x4003 1/10/100 ethernet product ABOCOM XX4 0x4004 XX4 product ABOCOM XX5 0x4007 XX5 product ABOCOM XX6 0x400b XX6 product ABOCOM XX7 0x400c XX7 product ABOCOM XX8 0x4102 XX8 product ABOCOM XX9 0x4104 XX9 product ABOCOM WL54 0x6001 WL54 product ABOCOM XX10 0xabc1 XX10 /* Accton products */ product ACCTON USB320_EC 0x1046 USB320-EC Ethernet product ACCTON SS1001 0x5046 SpeedStream Ethernet /* Acer Peripherals, Inc. products */ product ACERP ACERSCAN_C310U 0x12a6 Acerscan C310U product ACERP ACERSCAN_320U 0x2022 Acerscan 320U product ACERP ACERSCAN_640U 0x2040 Acerscan 640U product ACERP ACERSCAN_620U 0x2060 Acerscan 620U product ACERP ACERSCAN_640BT 0x20be Acerscan 640BT product ACERP ACERSCAN_1240U 0x20c0 Acerscan 1240U product ACERP AWL300 0x9000 AWL300 Wireless adapter product ACERP AWL400 0x9001 AWL400 Wireless adapter /* ActiveWire, Inc. products */ product ACTIVEWIRE IOBOARD 0x0100 I/O Board product ACTIVEWIRE IOBOARD_FW1 0x0101 I/O Board, rev. 1 firmware /* Actiontec, Inc. products */ product ACTIONTEC UAT1 0x7605 UAT1 Wireless Ethernet /* ADMtek products */ product ADMTEK PEGASUS 0x0986 AN986 Ethernet product ADMTEK PEGASUSII 0x8511 AN8511 Ethernet product ADMTEK PEGASUSII_2 0x8513 AN8513 Ethernet /* ADDON products */ /* PNY OEMs these */ product ADDON ATTACHE 0x1300 USB 2.0 Flash Drive product ADDON ATTACHE 0x1300 USB 2.0 Flash Drive product ADDON A256MB 0x1400 Attache 256MB USB 2.0 Flash Drive product ADDON DISKPRO512 0x1420 USB 2.0 Flash Drive (DANE-ELEC zMate 512MB USB flash drive) /* ADS products */ product ADS UBS10BT 0x0008 UBS-10BT Ethernet /* Agate Technologies products */ product AGATE QDRIVE 0x0378 Q-Drive /* AGFA products */ product AGFA SNAPSCAN1212U 0x0001 SnapScan 1212U product AGFA SNAPSCAN1236U 0x0002 SnapScan 1236U product AGFA SNAPSCANTOUCH 0x0100 SnapScan Touch product AGFA SNAPSCAN1212U2 0x2061 SnapScan 1212U product AGFA SNAPSCANE40 0x208d SnapScan e40 product AGFA SNAPSCANE50 0x208f SnapScan e50 product AGFA SNAPSCANE20 0x2091 SnapScan e20 product AGFA SNAPSCANE25 0x2095 SnapScan e25 product AGFA SNAPSCANE26 0x2097 SnapScan e26 product AGFA SNAPSCANE52 0x20fd SnapScan e52 /* Ain Communication Technology products */ product AINCOMM AWU2000B 0x1001 AWU2000B Wireless adapter /* AKS products */ product AKS USBHASP 0x0001 USB-HASP 0.06 /* Alcor Micro, Inc. products */ product ALCOR2 KBD_HUB 0x2802 Kbd Hub product ALCOR MA_KBD_HUB 0x9213 MacAlly Kbd Hub product ALCOR AU9814 0x9215 AU9814 Hub product ALCOR SM_KBD 0x9410 MicroConnectors/StrongMan Keyboard product ALCOR NEC_KBD_HUB 0x9472 NEC Kbd Hub /* Altec Lansing products */ product ALTEC ADA70 0x0070 ADA70 Speakers product ALTEC ASC495 0xff05 ASC495 Speakers /* American Power Conversion products */ product APC UPS 0x0002 Uninterruptible Power Supply /* Anchor products */ product ANCHOR EZUSB 0x2131 EZUSB product ANCHOR EZLINK 0x2720 EZLINK /* AOX, Inc. products */ product AOX USB101 0x0008 Ethernet /* Apple Computer products */ product APPLE OPTMOUSE 0x0302 Optical mouse product APPLE SPEAKERS 0x1101 Speakers product APPLE IPOD 0x1201 iPod product APPLE IPOD2G 0x1202 iPod 2G product APPLE IPOD3G 0x1203 iPod 3G product APPLE IPOD_04 0x1204 iPod '04' product APPLE IPODMINI 0x1205 iPod Mini product APPLE IPOD_06 0x1206 iPod '06' product APPLE IPOD_07 0x1207 iPod '07' product APPLE IPOD_08 0x1208 iPod '08' product APPLE IPODVIDEO 0x1209 iPod Video product APPLE IPODNANO 0x120a iPod Nano /* Asahi Optical products */ product ASAHIOPTICAL OPTIO230 0x0004 Digital camera product ASAHIOPTICAL OPTIO330 0x0006 Digital camera /* ASIX Electronics products */ product ASIX AX88172 0x1720 10/100 ethernet /* ASUS products */ product ASUS WL167G 0x1707 WL-167g wireless adapter product ASUS WL159G 0x170c WL-159g /* ATen products */ product ATEN UC1284 0x2001 Parallel printer product ATEN UC10T 0x2002 10Mbps ethernet product ATEN UC232A 0x2008 Serial /* Atmel Comp. products */ product ATMEL UHB124 0x3301 UHB124 hub product ATMEL DWL120 0x7603 DWL-120 Wireless adapter product ATMEL BW002 0x7605 BW002 Wireless adapter product ATMEL WL1130USB 0x7613 WL-1130 USB product ATMEL AT76C505A 0x7614 AT76c505a Wireless adapter /* Avision products */ product AVISION 1200U 0x0268 1200U scanner /* B&B Electronics products */ product BBELECTRONICS USOTL4 0xAC01 RS-422/485 /* Belkin products */ /*product BELKIN F5U111 0x???? F5U111 Ethernet*/ product BELKIN2 F5U002 0x0002 F5U002 Parallel printer product BELKIN USB2LAN 0x0121 USB to LAN product BELKIN F5U103 0x0103 F5U103 Serial product BELKIN F5U109 0x0109 F5U109 Serial product BELKIN F5U208 0x0208 F5U208 VideoBus II product BELKIN F5U409 0x0409 F5U409 Serial product BELKIN F5U120 0x1203 F5U120-PC Hub product BELKIN F5D7050 0x7050 F5D7050 wireless adapter product BELKIN F5D7050C 0x705c F5D705C 54g USB Network Adapter /* Billionton products */ product BILLIONTON USB100 0x0986 USB100N 10/100 FastEthernet product BILLIONTON USBLP100 0x0987 USB100LP product BILLIONTON USBEL100 0x0988 USB100EL product BILLIONTON USBE100 0x8511 USBE100 /* Broadcom products */ product BROADCOM BCM2033 0x2033 BCM2033 Bluetooth USB dongle /* Brother Industries products */ product BROTHER HL1050 0x0002 HL-1050 laser printer /* Behavior Technology Computer products */ product BTC BTC7932 0x6782 Keyboard with mouse port /* Canon, Inc. products */ product CANON N656U 0x2206 CanoScan N656U product CANON N1220U 0x2207 CanoScan N1220U product CANON D660U 0x2208 CanoScan D660U product CANON N676U 0x220d CanoScan N676U product CANON N1240U 0x220e CanoScan N1240U product CANON LIDE25 0x2220 CanoScan LIDE 25 product CANON S10 0x3041 PowerShot S10 product CANON S100 0x3045 PowerShot S100 product CANON S200 0x3065 PowerShot S200 /* CATC products */ product CATC NETMATE 0x000a Netmate ethernet product CATC NETMATE2 0x000c Netmate2 ethernet product CATC CHIEF 0x000d USB Chief Bus & Protocol Analyzer product CATC ANDROMEDA 0x1237 Andromeda hub /* CASIO products */ product CASIO NAMELAND 0x4001 CASIO Nameland EZ-USB /* Cherry products */ product CHERRY MY3000KBD 0x0001 My3000 keyboard product CHERRY MY3000HUB 0x0003 My3000 hub product CHERRY CYBOARD 0x0004 CyBoard Keyboard /* Chic Technology products */ product CHIC MOUSE1 0x0001 mouse product CHIC CYPRESS 0x0003 Cypress USB Mouse /* Chicony products */ product CHICONY KB8933 0x0001 KB-8933 keyboard /* Compaq products */ product COMPAQ PJB100 0x504a Personal Jukebox PJB100 /* Connectix products */ product CONNECTIX QUICKCAM 0x0001 QuickCam /* Corega products */ product COREGA ETHER_USB_T 0x0001 Ether USB-T product COREGA FETHER_USB_TX 0x0004 FEther USB-TX product COREGA FETHER_USB_TXS 0x000d FEther USB-TXS product COREGA FETHER_USB_TXC 0x9601 FEther USB-TXC /* Creative products */ product CREATIVE NOMAD_II 0x1002 Nomad II MP3 player /* Crystalfontz products */ product FTDI CFA_631 0xfc0c Crystalfontz CFA-631 USB LCD product FTDI CFA_632 0xfc08 Crystalfontz CFA-632 USB LCD product FTDI CFA_633 0xfc0b Crystalfontz CFA-633 USB LCD product FTDI CFA_634 0xfc09 Crystalfontz CFA-634 USB LCD product FTDI SEMC_DSS20 0xfc82 SEMC DSS-20 SyncStation /* Cambridge Silicon Radio Ltd. products */ product CSR BT_DONGLE 0x0001 Bluetooth USB dongle product CSR CSRDFU 0xffff USB Bluetooth Device in DFU State /* Conceptronic products */ product CONCEPTRONIC C54U 0x3c02 C54U wireless adapter /* CTX products */ product CTX EX1300 0x9999 Ex1300 hub /* CyberTAN Technology */ product CYBERTAN TG54USB 0x1666 TG54USB /* Cypress Semiconductor products */ product CYPRESS MOUSE 0x0001 mouse product CYPRESS THERMO 0x0002 thermometer product CYPRESS FMRADIO 0x1002 FM Radio product CYPRESS SLIM_HUB 0x6560 Slim Hub /* Daisy Technology products */ product DAISY DMC 0x6901 USB MultiMedia Reader /* Dallas Semiconductor products */ product DALLAS J6502 0x4201 J-6502 speakers /* Dell products */ product DELL PORT 0x0058 Port Replicator product DELL BC02 0x8000 BC02 Bluetooth USB Adapter /* Delorme Paublishing products */ product DELORME EARTHMATE 0x0100 Earthmate GPS /* Diamond products */ product DIAMOND RIO500USB 0x0001 Rio 500 USB /* Digi International products */ product DIGI ACCELEPORT2 0x0002 AccelePort USB 2 product DIGI ACCELEPORT4 0x0004 AccelePort USB 4 product DIGI ACCELEPORT8 0x0008 AccelePort USB 8 /* D-Link products */ /*product DLINK DSBS25 0x0100 DSB-S25 serial*/ product DLINK DUBE100 0x1a00 10/100 ethernet product DLINK DSB650TX4 0x200c 10/100 ethernet product DLINK DWLG122 0x3c00 DWL-G122 b1 wireless adapter product DLINK DSB650C 0x4000 10Mbps ethernet product DLINK DSB650TX1 0x4001 10/100 ethernet product DLINK DSB650TX 0x4002 10/100 ethernet product DLINK DSB650TX_PNA 0x4003 1/10/100 ethernet product DLINK DSB650TX3 0x400b 10/100 ethernet product DLINK DSB650TX2 0x4102 10/100 ethernet product DLINK DSB650 0xabc1 10/100 ethernet /* DrayTek products */ product DRAYTEK VIGOR550 0x0550 Vigor550 /* EIZO products */ product EIZO HUB 0x0000 hub product EIZO MONITOR 0x0001 monitor /* Elecom products */ product ELECOM MOUSE29UO 0x0002 mouse 29UO product ELECOM LDUSBTX0 0x200c LD-USB/TX product ELECOM LDUSBTX1 0x4002 LD-USB/TX product ELECOM LDUSBLTX 0x4005 LD-USBL/TX product ELECOM LDUSBTX2 0x400b LD-USB/TX product ELECOM UCSGT 0x5003 UC-SGT product ELECOM UCSGT0 0x5004 UC-SGT product ELECOM LDUSBTX3 0xabc1 LD-USB/TX /* Elsa products */ product ELSA MODEM1 0x2265 ELSA Modem Board product ELSA USB2ETHERNET 0x3000 Microlink USB2Ethernet /* EMS products */ product EMS DUAL_SHOOTER 0x0003 PSX gun controller converter /* Entrega products */ product ENTREGA 1S 0x0001 1S serial product ENTREGA 2S 0x0002 2S serial product ENTREGA 1S25 0x0003 1S25 serial product ENTREGA 4S 0x0004 4S serial product ENTREGA E45 0x0005 E45 Ethernet product ENTREGA CENTRONICS 0x0006 Parallel Port product ENTREGA 1S9 0x0093 1S9 serial product ENTREGA EZUSB 0x8000 EZ-USB /*product ENTREGA SERIAL 0x8001 DB25 Serial*/ product ENTREGA 2U4S 0x8004 2U4S serial/usb hub /*product ENTREGA SERIAL_DB9 0x8093 DB9 Serial*/ /* Epson products */ product EPSON PRINTER1 0x0001 USB Printer product EPSON PRINTER2 0x0002 ISD USB Smart Cable for Mac product EPSON PRINTER3 0x0003 ISD USB Smart Cable product EPSON PRINTER5 0x0005 USB Printer product EPSON 636 0x0101 Perfection 636U / 636Photo scanner product EPSON 610 0x0103 Perfection 610 scanner product EPSON 1200 0x0104 Perfection 1200U / 1200Photo scanner product EPSON 1600 0x0107 Expression 1600 scanner product EPSON 1640 0x010a Perfection 1640SU scanner product EPSON 1240 0x010b Perfection 1240U / 1240Photo scanner product EPSON 640U 0x010c Perfection 640U scanner product EPSON 1250 0x010f Perfection 1250U / 1250Photo scanner product EPSON 1650 0x0110 Perfection 1650 scanner product EPSON GT9700F 0x0112 GT-9700F scanner product EPSON GT9300UF 0x011b GT-9300UF scanner product EPSON 3200 0x011c Perfection 3200 scanner product EPSON 1260 0x011d Perfection 1260 scanner product EPSON 1660 0x011e Perfection 1660 scanner product EPSON 1670 0x011f Perfection 1670 scanner product EPSON 2480 0x0121 Perfection 2480 scanner product EPSON RX425 0x080f Stylus Photo RX425 scanner /* e-TEK Labs products */ product ETEK 1COM 0x8007 Serial /* Extended Systems products */ product EXTENDED XTNDACCESS 0x0100 XTNDAccess IrDA /* GoHubs products */ product GOHUBS GOCOM232 0x1001 GoCOM232 Serial /* Gravis products */ product GRAVIS GAMEPADPRO 0x4001 GamePad Pro /* GREENHOUSE products */ product GREENHOUSE KANA21 0x0001 CF-writer with MP3 /* Griffin Technology */ product GRIFFIN IMATE 0x0405 iMate, ADB adapter /* Freecom products */ product FREECOM DVD 0xfc01 DVD drive /* Future Technology Devices products */ product FTDI SERIAL_8U100AX 0x8372 8U100AX Serial product FTDI SERIAL_8U232AM 0x6001 8U232AM Serial product FTDI SERIAL_2232C 0x6010 FT2232C Dual port Serial /* Fuji photo products */ product FUJIPHOTO MASS0100 0x0100 Mass Storage /* Fujitsu protducts */ product FUJITSU AH_F401U 0x105b AH-F401U Air H device /* General Instruments (Motorola) products */ product GENERALINSTMNTS SB5100 0x5100 SURFboard SB5100 Cable modem /* Genesys Logic products */ product GENESYS GL650 0x0604 GL650 Hub product GENESYS GL641USB 0x0700 GL641USB CompactFlash Card Reader product GENESYS GL641USB2IDE_2 0x0701 GL641USB USB-IDE Bridge No 2 product GENESYS GL641USB2IDE 0x0702 GL641USB USB-IDE Bridge product GENESYS GL641USB_2 0x0760 GL641USB 6-in-1 Card Reader /* GIGABYTE products */ product GIGABYTE GNBR402W 0x8002 GN-BR402W product GIGABYTE GNWLBM101 0x8003 GN-WLBM101 product GIGABYTE GNWBKG 0x8007 GN-WBKG /* G.Mate, Inc products */ product GMATE YP3X00 0x1001 YP3X00 PDA /* Guillemot Corporation */ product GUILLEMOT DALEADER 0xa300 DA Leader product GUILLEMOT HWGUSB254 0xe000 HWGUSB2-54 WLAN /* HAL Corporation products */ product HAL IMR001 0x0011 Crossam2+USB IR commander /* Hagiwara products */ product HAGIWARA FGSM 0x0002 FlashGate SmartMedia Card Reader product HAGIWARA FGCF 0x0003 FlashGate CompactFlash Card Reader product HAGIWARA FG 0x0005 FlashGate /* Handspring, Inc. */ product HANDSPRING VISOR 0x0100 Handspring Visor product HANDSPRING TREO 0x0200 Handspring Treo product HANDSPRING TREO600 0x0300 Handspring Treo 600 /* Hauppauge Computer Works */ product HAUPPAUGE WINTV_USB_FM 0x4d12 WinTV USB FM /* Hawking Technologies products */ product HAWKING UF100 0x400c 10/100 USB Ethernet /* Hitachi, Ltd. products */ product HITACHI DVDCAM_USB 0x001e DVDCAM USB HS Interface /* HP products */ product HP 895C 0x0004 DeskJet 895C product HP 4100C 0x0101 Scanjet 4100C product HP S20 0x0102 Photosmart S20 product HP 880C 0x0104 DeskJet 880C product HP 4200C 0x0105 ScanJet 4200C product HP CDWRITERPLUS 0x0107 CD-Writer Plus product HP KBDHUB 0x010c Multimedia Keyboard Hub product HP G55XI 0x0111 OfficeJet G55xi product HP HN210W 0x011c HN210W 802.11b WLAN product HP 49GPLUS 0x0121 49g+ graphing calculator product HP 6200C 0x0201 ScanJet 6200C product HP S20b 0x0202 PhotoSmart S20 product HP 815C 0x0204 DeskJet 815C product HP 3300C 0x0205 ScanJet 3300C product HP CDW8200 0x0207 CD-Writer Plus 8200e product HP MMKEYB 0x020c Multimedia keyboard product HP 1220C 0x0212 DeskJet 1220C product HP 810C 0x0304 DeskJet 810C/812C product HP 4300C 0x0305 Scanjet 4300C product HP G85XI 0x0311 OfficeJet G85xi product HP 1200 0x0317 LaserJet 1200 product HP 5200C 0x0401 Scanjet 5200C product HP 830C 0x0404 DeskJet 830C product HP 3400CSE 0x0405 ScanJet 3400cse product HP 6300C 0x0601 Scanjet 6300C product HP 840C 0x0604 DeskJet 840c product HP 2200C 0x0605 ScanJet 2200C product HP 5300C 0x0701 Scanjet 5300C product HP 4400C 0x0705 Scanjet 4400C product HP 82x0C 0x0b01 Scanjet 82x0C product HP 2300D 0x0b17 Laserjet 2300d product HP 970CSE 0x1004 Deskjet 970Cse product HP 5400C 0x1005 Scanjet 5400C product HP 930C 0x1204 DeskJet 930c product HP P2000U 0x1801 Inkjet P-2000U product HP 640C 0x2004 DeskJet 640c product HP 4670V 0x3005 ScanJet 4670v product HP P1100 0x3102 Photosmart P1100 product HP HN210E 0x811c Ethernet HN210E /* HP products */ product HP2 C500 0x6002 PhotoSmart C500 /* IBM Corporation */ product IBM USBCDROMDRIVE 0x4427 USB CD-ROM Drive /* Imagination Technologies products */ product IMAGINATION DBX1 0x2107 DBX1 DSP core /* Inside Out Networks products */ product INSIDEOUT EDGEPORT4 0x0001 EdgePort/4 serial ports /* In-System products */ product INSYSTEM F5U002 0x0002 Parallel printer product INSYSTEM ATAPI 0x0031 ATAPI adapter product INSYSTEM ISD110 0x0200 IDE adapter ISD110 product INSYSTEM ISD105 0x0202 IDE adapter ISD105 product INSYSTEM USBCABLE 0x081a USB cable /* Intel products */ product INTEL EASYPC_CAMERA 0x0110 Easy PC Camera product INTEL TESTBOARD 0x9890 82930 test board /* Intersil products */ product INTERSIL PRISM_2X 0x3642 Prism2.x or Atmel WLAN /* Interpid Control Systems products */ product INTREPIDCS VALUECAN 0x0601 ValueCAN CAN bus interface product INTREPIDCS NEOVI 0x0701 NeoVI Blue vehicle bus interface /* I/O DATA products */ product IODATA IU_CD2 0x0204 DVD Multi-plus unit iU-CD2 product IODATA DVR_UEH8 0x0206 DVD Multi-plus unit DVR-UEH8 product IODATA USBETT 0x0901 USB ETT product IODATA USBETTX 0x0904 USB ETTX product IODATA USBETTXS 0x0913 USB ETTX product IODATA USBRSAQ 0x0a03 Serial USB-RSAQ1 /* Iomega products */ product IOMEGA ZIP100 0x0001 Zip 100 product IOMEGA ZIP250 0x0030 Zip 250 /* JVC products */ product JVC GR_DX95 0x000a GR-DX95 product JVC MP_PRX1 0x3008 MP-PRX1 Ethernet /* JRC products */ product JRC AH_J3001V_J3002V 0x0001 AirH PHONE AH-J3001V/J3002V /* Kawasaki products */ product KLSI DUH3E10BT 0x0008 USB ethernet product KLSI DUH3E10BTN 0x0009 USB ethernet /* Kawatsu products */ product KAWATSU MH4000P 0x0003 MiniHub 4000P /* Keisokugiken Corp. products */ product KEISOKUGIKEN USBDAQ 0x0068 HKS-0200 USBDAQ /* Kawasaki products */ product KLSI DUH3E10BT 0x0008 DU-H3E 10BT Ethernet /* Kensington products */ product KENSINGTON ORBIT 0x1003 Orbit USB/PS2 trackball product KENSINGTON TURBOBALL 0x1005 TurboBall /* Keyspan products */ product KEYSPAN USA28_NF 0x0101 USA-28 serial adapter (no firmware) product KEYSPAN USA28X_NF 0x0102 USA-28X serial adapter (no firmware) product KEYSPAN USA19_NF 0x0103 USA-19 serial adapter (no firmware) product KEYSPAN USA18_NF 0x0104 USA-18 serial adapter (no firmware) product KEYSPAN USA18X_NF 0x0105 USA-18X serial adapter (no firmware) product KEYSPAN USA19W_NF 0x0106 USA-19W serial adapter (no firmware) product KEYSPAN USA19 0x0107 USA-19 serial adapter product KEYSPAN USA19W 0x0108 USA-19W serial adapter product KEYSPAN USA49W_NF 0x0109 USA-49W serial adapter (no firmware) product KEYSPAN USA49W 0x010a USA-49W serial adapter product KEYSPAN USA19QI_NF 0x010b USA-19QI serial adapter (no firmware) product KEYSPAN USA19QI 0x010c USA-19QI serial adapter product KEYSPAN USA19Q_NF 0x010d USA-19Q serial adapter (no firmware) product KEYSPAN USA19Q 0x010e USA-19Q serial adapter product KEYSPAN USA28 0x010f USA-28 serial adapter product KEYSPAN USA28XXB 0x0110 USA-28X/XB serial adapter product KEYSPAN USA18 0x0111 USA-18 serial adapter product KEYSPAN USA18X 0x0112 USA-18X serial adapter product KEYSPAN USA28XB_NF 0x0113 USA-28XB serial adapter (no firmware) product KEYSPAN USA28XA_NF 0x0114 USA-28XB serial adapter (no firmware) product KEYSPAN USA28XA 0x0115 USA-28XA serial adapter product KEYSPAN USA18XA_NF 0x0116 USA-18XA serial adapter (no firmware) product KEYSPAN USA18XA 0x0117 USA-18XA serial adapter product KEYSPAN USA19QW_NF 0x0118 USA-19WQ serial adapter (no firmware) product KEYSPAN USA19QW 0x0119 USA-19WQ serial adapter product KEYSPAN USA19HA 0x0121 USA-19HS serial adapter product KEYSPAN UIA10 0x0201 UIA-10 remote control product KEYSPAN UIA11 0x0202 UIA-11 remote control /* Kingston products */ product KINGSTON KNU101TX 0x000a KNU101TX USB Ethernet /* Kodak products */ product KODAK DC220 0x0100 Digital Science DC220 product KODAK DC260 0x0110 Digital Science DC260 product KODAK DC265 0x0111 Digital Science DC265 product KODAK DC290 0x0112 Digital Science DC290 product KODAK DC240 0x0120 Digital Science DC240 product KODAK DC280 0x0130 Digital Science DC280 /* Konica Corp. Products */ product KONICA CAMERA 0x0720 Digital Color Camera /* KYE products */ product KYE NICHE 0x0001 Niche mouse product KYE NETSCROLL 0x0003 Genius NetScroll mouse product KYE FLIGHT2000 0x1004 Flight 2000 joystick product KYE VIVIDPRO 0x2001 ColorPage Vivid-Pro scanner /* Kyocera products */ product KYOCERA AHK3001V 0x0203 AH-K3001V /* LaCie products */ product LACIE HD 0xa601 Hard Disk product LACIE CDRW 0xa602 CD R/W /* Lexar products */ product LEXAR JUMPSHOT 0x0001 jumpSHOT CompactFlash Reader /* Lexmark products */ product LEXMARK S2450 0x0009 Optra S 2450 /* Linksys products */ product LINKSYS MAUSB2 0x0105 Camedia MAUSB-2 product LINKSYS USB10TX1 0x200c USB10TX product LINKSYS USB10T 0x2202 USB10T Ethernet product LINKSYS USB100TX 0x2203 USB100TX Ethernet product LINKSYS USB100H1 0x2204 USB100H1 Ethernet/HPNA product LINKSYS USB10TA 0x2206 USB10TA Ethernet product LINKSYS USB10TX2 0x400b USB10TX product LINKSYS2 WUSB11 0x2219 WUSB11 Wireless adapter product LINKSYS2 USB200M 0x2226 USB 2.0 10/100 ethernet product LINKSYS3 WUSB11v28 0x2233 WUSB11 v2.8 wireless adapter product LINKSYS4 WUSB54G 0x000d WUSB54G wireless adapter product LINKSYS4 WUSB54GP 0x0011 WUSB54GP wireless adapter product LINKSYS4 HU200TS 0x001a HU200TS wireless adapter /* Logitech products */ product LOGITECH M2452 0x0203 M2452 keyboard product LOGITECH M4848 0x0301 M4848 mouse product LOGITECH PAGESCAN 0x040f PageScan product LOGITECH QUICKCAMWEB 0x0801 QuickCam Web product LOGITECH QUICKCAMPRO 0x0810 QuickCam Pro product LOGITECH QUICKCAMEXP 0x0840 QuickCam Express product LOGITECH QUICKCAM 0x0850 QuickCam product LOGITECH N43 0xc000 N43 product LOGITECH N48 0xc001 N48 mouse product LOGITECH MBA47 0xc002 M-BA47 mouse product LOGITECH WMMOUSE 0xc004 WingMan Gaming Mouse product LOGITECH BD58 0xc00c BD58 mouse product LOGITECH UN58A 0xc030 iFeel Mouse product LOGITECH BB13 0xc401 USB-PS/2 Trackball product LOGITECH WMPAD 0xc208 WingMan GamePad Extreme product LOGITECH WMRPAD 0xc20a WingMan RumblePad product LOGITECH WMJOY 0xc281 WingMan Force joystick product LOGITECH RK53 0xc501 Cordless mouse product LOGITECH RB6 0xc503 Cordless keyboard product LOGITECH MX700 0xc506 Cordless optical mouse product LOGITECH QUICKCAMPRO2 0xd001 QuickCam Pro /* Logitec Corp. products */ product LOGITEC LDR_H443SU2 0x0033 DVD Multi-plus unit LDR-H443SU2 product LOGITEC LDR_H443U2 0x00b3 DVD Multi-plus unit LDR-H443U2 /* Lucent products */ product LUCENT EVALKIT 0x1001 USS-720 evaluation kit /* Luwen products */ product LUWEN EASYDISK 0x0005 EasyDisc /* Macally products */ product MACALLY MOUSE1 0x0101 mouse /* Matrix Orbital products */ product FTDI USBSERIAL 0xfa00 Matrix Orbital USB Serial product FTDI MX2_3 0xfa01 Matrix Orbital MX2 or MX3 product FTDI MX4_5 0xfa02 Matrix Orbital MX4 or MX5 product FTDI LK202 0xfa03 Matrix Orbital VK/LK202 Family product FTDI LK204 0xfa04 Matrix Orbital VK/LK204 Family /* MCT Corp. */ product MCT HUB0100 0x0100 Hub product MCT DU_H3SP_USB232 0x0200 D-Link DU-H3SP USB BAY Hub product MCT USB232 0x0210 USB-232 Interface product MCT SITECOM_USB232 0x0230 Sitecom USB-232 Products /* Melco, Inc products */ product MELCO LUATX1 0x0001 LUA-TX Ethernet product MELCO LUATX5 0x0005 LUA-TX Ethernet product MELCO LUA2TX5 0x0009 LUA2-TX Ethernet product MELCO LUAKTX 0x0012 LUA-KTX Ethernet product MELCO DUBPXXG 0x001c USB-IDE Bridge: DUB-PxxG product MELCO LUAU2KTX 0x003d LUA-U2-KTX Ethernet product MELCO KG54YB 0x005e WLI-U2-KG54-YB WLAN product MELCO KG54 0x0066 WLI-U2-KG54 WLAN product MELCO KG54AI 0x0067 WLI-U2-KG54-AI WLAN product MELCO NINWIFI 0x008b Nintendo Wi-Fi /* Metricom products */ product METRICOM RICOCHET_GS 0x0001 Ricochet GS /* MGE UPS Systems */ product MGE UPS1 0x0001 MGE UPS SYSTEMS PROTECTIONCENTER 1 product MGE UPS2 0xffff MGE UPS SYSTEMS PROTECTIONCENTER 2 /* Micro Star International products */ product MSI BT_DONGLE 0x1967 Bluetooth USB dongle product MSI RT2570 0x6861 RT2570 product MSI RT2570_2 0x6865 RT2570 product MSI RT2570_3 0x6869 RT2570 /* Microsoft products */ product MICROSOFT SIDEPREC 0x0008 SideWinder Precision Pro product MICROSOFT INTELLIMOUSE 0x0009 IntelliMouse product MICROSOFT NATURALKBD 0x000b Natural Keyboard Elite product MICROSOFT DDS80 0x0014 Digital Sound System 80 product MICROSOFT SIDEWINDER 0x001a Sidewinder Precision Racing Wheel product MICROSOFT INETPRO 0x001c Internet Keyboard Pro product MICROSOFT INTELLIEYE 0x0025 IntelliEye mouse product MICROSOFT INETPRO2 0x002b Internet Keyboard Pro product MICROSOFT MN110 0x007a 10/100 USB NIC /* Microtech products */ product MICROTECH SCSIDB25 0x0004 USB-SCSI-DB25 product MICROTECH SCSIHD50 0x0005 USB-SCSI-HD50 product MICROTECH DPCM 0x0006 USB CameraMate product MICROTECH FREECOM 0xfc01 Freecom USB-IDE /* Microtek products */ product MICROTEK 336CX 0x0094 Phantom 336CX - C3 scanner product MICROTEK X6U 0x0099 ScanMaker X6 - X6U product MICROTEK C6 0x009a Phantom C6 scanner product MICROTEK 336CX2 0x00a0 Phantom 336CX - C3 scanner product MICROTEK V6USL 0x00a3 ScanMaker V6USL product MICROTEK V6USL2 0x80a3 ScanMaker V6USL product MICROTEK V6UL 0x80ac ScanMaker V6UL /* Microtune, Inc. products */ product MICROTUNE BT_DONGLE 0x1000 Bluetooth USB dongle /* Midiman products */ product MIDIMAN MIDISPORT2X2 0x1001 Midisport 2x2 /* Minolta Co., Ltd. */ product MINOLTA 2300 0x4001 Dimage 2300 product MINOLTA S304 0x4007 Dimage S304 product MINOLTA X 0x4009 Dimage X product MINOLTA 5400 0x400e Dimage 5400 /* Mitsumi products */ product MITSUMI CDRRW 0x0000 CD-R/RW Drive product MITSUMI BT_DONGLE 0x641f Bluetooth USB dongle /* Motorola products */ product MOTOROLA MC141555 0x1555 MC141555 hub controller product MOTOROLA SB4100 0x4100 SB4100 USB Cable Modem product MOTOROLA2 E398 0x4810 E398 Mobile Phone /* MultiTech products */ product MULTITECH ATLAS 0xf101 MT5634ZBA-USB modem /* Mustek products */ product MUSTEK 1200CU 0x0001 1200 CU scanner product MUSTEK 600CU 0x0002 600 CU scanner product MUSTEK 1200USB 0x0003 1200 USB scanner product MUSTEK 1200UB 0x0006 1200 UB scanner product MUSTEK 1200USBPLUS 0x0007 1200 USB Plus scanner product MUSTEK 1200CUPLUS 0x0008 1200 CU Plus scanner product MUSTEK BEARPAW1200F 0x0010 BearPaw 1200F scanner product MUSTEK BEARPAW1200TA 0x021e BearPaw 1200TA scanner product MUSTEK 600USB 0x0873 600 USB scanner product MUSTEK MDC800 0xa800 MDC-800 digital camera /* M-Systems products */ product MSYSTEMS DISKONKEY 0x0010 DiskOnKey product MSYSTEMS DISKONKEY2 0x0011 DiskOnKey /* National Semiconductor */ product NATIONAL BEARPAW1200 0x1000 BearPaw 1200 product NATIONAL BEARPAW2400 0x1001 BearPaw 2400 /* NEC products */ product NEC HUB 0x55aa hub product NEC HUB_B 0x55ab hub /* NEODIO products */ product NEODIO ND3260 0x3260 8-in-1 Multi-format Flash Controller product NEODIO ND5010 0x5010 Multi-format Flash Controller /* NetChip Technology Products */ product NETCHIP TURBOCONNECT 0x1080 Turbo-Connect product NETCHIP ETHERNETGADGET 0xa4a2 Linux Ethernet/RNDIS gadget on pxa210/25x/26x /* Netgear products */ product NETGEAR EA101 0x1001 Ethernet product NETGEAR FA120 0x1040 USB 2.0 Ethernet /* Nikon products */ product NIKON E990 0x0102 Digital Camera E990 product NIKON LS40 0x4000 CoolScan LS40 ED /* Olympus products */ product OLYMPUS C1 0x0102 C-1 Digital Camera product OLYMPUS C700 0x0105 C-700 Ultra Zoom /* OmniVision Technologies, Inc. products */ product OMNIVISION OV511 0x0511 OV511 Camera product OMNIVISION OV511PLUS 0xa511 OV511+ Camera /* OnSpec Electronic, Inc. */ product ONSPEC UCF100 0xa400 FlashLink UCF-100 CompactFlash Reader /* Palm Computing, Inc. product */ product PALM SERIAL 0x0080 USB Serial product PALM M500 0x0001 Palm m500 product PALM M505 0x0002 Palm m505 product PALM M515 0x0003 Palm m515 product PALM I705 0x0020 Palm i705 product PALM TUNGSTEN_Z 0x0031 Palm Tungsten Z product PALM M125 0x0040 Palm m125 product PALM M130 0x0050 Palm m130 product PALM TUNGSTEN_T 0x0060 Palm Tungsten T product PALM ZIRE31 0x0061 Palm Zire 31 product PALM ZIRE 0x0070 Palm Zire /* Panasonic products */ product PANASONIC KXLRW32AN 0x0d09 CD-R Drive KXL-RW32AN product PANASONIC KXLCB20AN 0x0d0a CD-R Drive KXL-CB20AN product PANASONIC KXLCB35AN 0x0d0e DVD-ROM & CD-R/RW product PANASONIC SDCAAE 0x1b00 MultiMediaCard /* Peracom products */ product PERACOM SERIAL1 0x0001 Serial product PERACOM ENET 0x0002 Ethernet product PERACOM ENET3 0x0003 At Home Ethernet product PERACOM ENET2 0x0005 Ethernet /* Philips products */ product PHILIPS DSS350 0x0101 DSS 350 Digital Speaker System product PHILIPS DSS 0x0104 DSS XXX Digital Speaker System product PHILIPS HUB 0x0201 hub product PHILIPS PCA646VC 0x0303 PCA646VC PC Camera product PHILIPS PCVC680K 0x0308 PCVC680K Vesta Pro PC Camera product PHILIPS DSS150 0x0471 DSS 150 Digital Speaker System product PHILIPS UM10016 0x1552 ISP 1581 Hi-Speed USB MPEG2 Encoder Reference Kit product PHILIPS DIVAUSB 0x1801 DIVA USB mp3 player /* Philips Semiconductor products */ product PHILIPSSEMI HUB1122 0x1122 hub /* P.I. Engineering products */ product PIENGINEERING PS2USB 0x020b PS2 to Mac USB Adapter /* Planex Communications products */ product PLANEX3 GWUS54MINI 0xab13 GW-US54Mini product PLANEX2 GWUS54GZL 0xc007 GW-US54GZL /* Plextor Corp. */ product PLEXTOR 40_12_40U 0x0011 PlexWriter 40/12/40U /* PLX products */ product PLX TESTBOARD 0x9060 test board /* PNY products */ product PNY ATTACHE2 0x0010 USB 2.0 Flash Drive /* Primax products */ product PRIMAX G2X300 0x0300 G2-200 scanner product PRIMAX G2E300 0x0301 G2E-300 scanner product PRIMAX G2300 0x0302 G2-300 scanner product PRIMAX G2E3002 0x0303 G2E-300 scanner product PRIMAX 9600 0x0340 Colorado USB 9600 scanner product PRIMAX 600U 0x0341 Colorado 600u scanner product PRIMAX 6200 0x0345 Visioneer 6200 scanner product PRIMAX 19200 0x0360 Colorado USB 19200 scanner product PRIMAX 1200U 0x0361 Colorado 1200u scanner product PRIMAX G600 0x0380 G2-600 scanner product PRIMAX 636I 0x0381 ReadyScan 636i product PRIMAX G2600 0x0382 G2-600 scanner product PRIMAX G2E600 0x0383 G2E-600 scanner product PRIMAX COMFORT 0x4d01 Comfort product PRIMAX MOUSEINABOX 0x4d02 Mouse-in-a-Box product PRIMAX PCGAUMS1 0x4d04 Sony PCGA-UMS1 /* Prolific products */ product PROLIFIC PL2301 0x0000 PL2301 Host-Host interface product PROLIFIC PL2302 0x0001 PL2302 Host-Host interface product PROLIFIC RSAQ2 0x04bb PL2303 Serial (IODATA USB-RSAQ2) product PROLIFIC PL2303 0x2303 PL2303 Serial (ATEN/IOGEAR UC232A) product PROLIFIC PL2305 0x2305 Parallel printer product PROLIFIC ATAPI4 0x2307 ATAPI-4 Controller product PROLIFIC PL2501 0x2501 PL2501 Host-Host interface product PROLIFIC RSAQ3 0xaaa2 PL2303 Serial adapter (IODATA USB-RSAQ3) /* Putercom products */ product PUTERCOM UPA100 0x047e USB-1284 BRIDGE /* Qualcomm products */ product QUALCOMM CDMA_MSM 0x3196 CDMA Technologies MSM modem product QUALCOMM2 CDMA_MSM 0x6000 CDMA Technologies MSM phone /* Qtronix products */ product QTRONIX 980N 0x2011 Scorpion-980N keyboard /* Quickshot products */ product QUICKSHOT STRIKEPAD 0x6238 USB StrikePad /* Rainbow Technologies products */ product RAINBOW IKEY2000 0x1200 i-Key 2000 /* Ralink Technology products */ product RALINK RT2570 0x1706 RT2500USB wireless adapter product RALINK RT2570_2 0x2570 RT2500USB wireless adapter /* ReakTek products */ product REALTEK USBKR100 0x8150 USBKR100 USB Ethernet (GREEN HOUSE) /* Roland products */ product ROLAND UM1 0x0009 UM-1 MIDI I/F product ROLAND UM880N 0x0014 EDIROL UM-880 MIDI I/F (native) product ROLAND UM880G 0x0015 EDIROL UM-880 MIDI I/F (generic) /* Rockfire products */ product ROCKFIRE GAMEPAD 0x2033 gamepad 203USB /* RATOC Systems products */ product RATOC REXUSB60 0xb000 REX-USB60 /* Sagem products */ product SAGEM XG760A 0x004a XG-760A /* Samsung products */ product SAMSUNG ML6060 0x3008 ML-6060 laser printer /* SanDisk products */ product SANDISK SDDR05A 0x0001 ImageMate SDDR-05a product SANDISK SDDR05 0x0005 ImageMate SDDR-05 product SANDISK SDDR31 0x0002 ImageMate SDDR-31 product SANDISK SDDR12 0x0100 ImageMate SDDR-12 product SANDISK SDDR09 0x0200 ImageMate SDDR-09 product SANDISK SDDR75 0x0810 ImageMate SDDR-75 product SANDISK SDCZ2_256 0x7104 Cruzer Mini 256MB product SANDISK SDCZ4_128 0x7112 Cruzer Micro 128MB product SANDISK SDCZ4_256 0x7113 Cruzer Micro 256MB /* Sanyo Electric products */ product SANYO SCP4900 0x0701 Sanyo SCP-4900 USB Phone /* ScanLogic products */ product SCANLOGIC SL11R 0x0002 SL11R IDE Adapter product SCANLOGIC 336CX 0x0300 Phantom 336CX - C3 scanner /* Sharp products */ product SHARP SL5500 0x8004 Zaurus SL-5500 PDA product SHARP SLA300 0x8005 Zaurus SL-A300 PDA product SHARP SL5600 0x8006 Zaurus SL-5600 PDA product SHARP SLC700 0x8007 Zaurus SL-C700 PDA product SHARP SLC750 0x9031 Zaurus SL-C750 PDA /* Shuttle Technology products */ product SHUTTLE EUSB 0x0001 E-USB Bridge product SHUTTLE EUSCSI 0x0002 eUSCSI Bridge product SHUTTLE SDDR09 0x0003 ImageMate SDDR09 product SHUTTLE ZIOMMC 0x0006 eUSB MultiMediaCard Adapter product SHUTTLE HIFD 0x0007 Sony Hifd product SHUTTLE EUSBATAPI 0x0009 eUSB ATA/ATAPI Adapter product SHUTTLE CF 0x000a eUSB CompactFlash Adapter product SHUTTLE EUSCSI_B 0x000b eUSCSI Bridge product SHUTTLE EUSCSI_C 0x000c eUSCSI Bridge product SHUTTLE CDRW 0x0101 CD-RW Device product SHUTTLE EUSBORCA 0x0325 eUSB ORCA Quad Reader /* Siemens products */ product SIEMENS SPEEDSTREAM 0x1001 SpeedStream USB /* Sigmatel products */ product SIGMATEL I_BEAD100 0x8008 i-Bead 100 MP3 Player /* SIIG products */ product SIIG DIGIFILMREADER 0x0004 DigiFilm-Combo Reader product SIIG WINTERREADER 0x0330 WINTERREADER Reader product SIIG2 US2308 0x0421 Serial /* Silicon Portals Inc. */ product SILICONPORTALS YAPPH_NF 0x0200 YAP Phone (no firmware) product SILICONPORTALS YAPPHONE 0x0201 YAP Phone /* Sirius Technologies products */ product SIRIUS ROADSTER 0x0001 NetComm Roadster II 56 USB /* Sitecom products */ product SITECOM LN029 0x182d USB 2.0 Ethernet product SITECOM SERIAL 0x2068 USB to serial cable (v2) /* Sitecom Europe products */ product SITECOMEU WL113 0x9071 WL-113 /* SmartBridges products */ product SMARTBRIDGES SMARTLINK 0x0001 SmartLink USB ethernet product SMARTBRIDGES SMARTNIC 0x0003 smartNIC 2 PnP ethernet /* SMC products */ product SMC 2102USB 0x0100 10Mbps ethernet product SMC 2202USB 0x0200 10/100 ethernet product SMC 2206USB 0x0201 EZ Connect USB Ethernet product SMC 2862WG 0xee13 EZ Connect wireless adapter product SMC2 2020HUB 0x2020 USB Hub product SMC3 2662WUSB 0xa002 2662W-AR Wireless /* SOHOware products */ product SOHOWARE NUB100 0x9100 10/100 USB Ethernet /* SOLID YEAR products */ product SOLIDYEAR KEYBOARD 0x2101 Solid Year USB keyboard /* SONY products */ product SONY DSC 0x0010 DSC cameras product SONY MSACUS1 0x002d Memorystick MSAC-US1 product SONY HANDYCAM 0x002e Handycam product SONY MSC 0x0032 MSC memory stick slot product SONY CLIE_35 0x0038 Sony Clie v3.5 product SONY CLIE_40 0x0066 Sony Clie v4.0 product SONY CLIE_40_MS 0x006d Sony Clie v4.0 Memory Stick slot product SONY CLIE_S360 0x0095 Sony Clie s360 product SONY CLIE_41_MS 0x0099 Sony Clie v4.1 Memory Stick slot product SONY CLIE_41 0x009a Sony Clie v4.1 product SONY CLIE_NX60 0x00da Sony Clie nx60 product SONY CLIE_TJ37 0x0169 Sony Clie tj37 /* Sony Ericsson products */ product SONYERICSSON DCU10 0x0528 USB Cable /* SOURCENEXT products */ product SOURCENEXT KEIKAI8 0x039f KeikaiDenwa 8 product SOURCENEXT KEIKAI8_CHG 0x012e KeikaiDenwa 8 with charger /* STMicroelectronics products */ product STMICRO BIOCPU 0x2016 Biometric Coprocessor product STMICRO COMMUNICATOR 0x7554 USB Communicator /* STSN products */ product STSN STSN0001 0x0001 Internet Access Device /* SUN Corporation products */ product SUNTAC DS96L 0x0003 SUNTAC U-Cable type D2 product SUNTAC PS64P1 0x0005 SUNTAC U-Cable type P1 product SUNTAC VS10U 0x0009 SUNTAC Slipper U product SUNTAC IS96U 0x000a SUNTAC Ir-Trinity product SUNTAC AS64LX 0x000b SUNTAC U-Cable type A3 product SUNTAC AS144L4 0x0011 SUNTAC U-Cable type A4 /* Sun Microsystems products */ product SUN2 KEYBOARD 0x0005 Type 6 USB keyboard /* XXX The above is a North American PC style keyboard possibly */ product SUN2 MOUSE 0x0100 Type 6 USB mouse /* Supra products */ product DIAMOND2 SUPRAEXPRESS56K 0x07da Supra Express 56K modem product DIAMOND2 SUPRA2890 0x0b4a SupraMax 2890 56K Modem product DIAMOND2 RIO600USB 0x5001 Rio 600 USB product DIAMOND2 RIO800USB 0x5002 Rio 800 USB /* Sweex products */ product SWEEX ZD1211 0x1809 ZD1211 /* System TALKS, Inc. */ product SYSTEMTALKS SGCX2UL 0x1920 SGC-X2UL /* Taugagreining products */ product TAUGA CAMERAMATE 0x0005 CameraMate (DPCM_USB) /* TDK products */ product TDK UPA9664 0x0115 USB-PDC Adapter UPA9664 product TDK UCA1464 0x0116 USB-cdmaOne Adapter UCA1464 product TDK UHA6400 0x0117 USB-PHS Adapter UHA6400 product TDK UPA6400 0x0118 USB-PHS Adapter UPA6400 product TDK BT_DONGLE 0x0309 Bluetooth USB dongle /* TEAC products */ product TEAC FD05PUB 0x0000 FD-05PUB floppy /* Tekram Technology products */ product TEKRAM QUICKWLAN 0x1630 QuickWLAN product TEKRAM ZD1211 0x5630 ZD1211 /* Telex Communications products */ product TELEX MIC1 0x0001 Enhanced USB Microphone /* Texas Intel products */ product TI UTUSB41 0x1446 UT-USB41 hub product TI TUSB2046 0x2046 TUSB2046 hub /* Thrustmaster products */ product THRUST FUSION_PAD 0xa0a3 Fusion Digital Gamepad /* Toshiba Corporation products */ product TOSHIBA POCKETPC_E740 0x0706 PocketPC e740 /* Trek Technology products */ product TREK THUMBDRIVE 0x1111 ThumbDrive product TREK THUMBDRIVE_8MB 0x9988 ThumbDrive_8MB /* Tripp-Lite products */ product TRIPPLITE U209 0x2008 Serial /* Trumpion products */ product TRUMPION C3310 0x1100 Comotron C3310 MP3 player /* TwinMOS products */ product TWINMOS MDIV 0x1325 Memory Disk IV product TWINMOS G240 0xa006 G240 /* Ultima products */ product ULTIMA 1200UBPLUS 0x4002 1200 UB Plus scanner /* UMAX products */ product UMAX ASTRA1236U 0x0002 Astra 1236U Scanner product UMAX ASTRA1220U 0x0010 Astra 1220U Scanner product UMAX ASTRA2000U 0x0030 Astra 2000U Scanner product UMAX ASTRA2100U 0x0130 Astra 2100U Scanner product UMAX ASTRA2200U 0x0230 Astra 2200U Scanner product UMAX ASTRA3400 0x0060 Astra 3400 Scanner /* U-MEDIA Communications products */ product UMEDIA TEW429UB_A 0x300a TEW-429UB_A product UMEDIA TEW429UB 0x300b TEW-429UB /* Universal Access products */ product UNIACCESS PANACHE 0x0101 Panache Surf USB ISDN Adapter /* VidzMedia products */ product VIDZMEDIA MONSTERTV 0x4fb1 MonsterTV P2H /* Vision products */ product VISION VC6452V002 0x0002 CPiA Camera /* Visioneer products */ product VISIONEER 7600 0x0211 OneTouch 7600 product VISIONEER 5300 0x0221 OneTouch 5300 product VISIONEER 3000 0x0224 Scanport 3000 product VISIONEER 6100 0x0231 OneTouch 6100 product VISIONEER 6200 0x0311 OneTouch 6200 product VISIONEER 8100 0x0321 OneTouch 8100 product VISIONEER 8600 0x0331 OneTouch 8600 /* Vodafone products */ product VODAFONE MC3G 0x5000 Mobile Connect 3G datacard /* VTech products */ product VTECH RT2570 0x3012 RT2570 /* Wacom products */ product WACOM CT0405U 0x0000 CT-0405-U Tablet product WACOM GRAPHIRE 0x0010 Graphire product WACOM GRAPHIRE3_4X5 0x0013 Graphire 3 4x5 product WACOM INTUOSA5 0x0021 Intuos A5 product WACOM GD0912U 0x0022 Intuos 9x12 Graphics Tablet /* Western Digital products */ product WESTERN EXTHDD 0x0400 External HDD product WESTERN HUB 0x0500 USB HUB /* Windbond Electronics */ product WINBOND UH104 0x5518 4-port USB Hub /* Wistron NeWeb products */ product WISTRONNEWEB UR055G 0x0711 UR055G /* Xirlink products */ product XIRLINK PCCAM 0x8080 IBM PC Camera /* Y-E Data products */ product YEDATA FLASHBUSTERU 0x0000 Flashbuster-U /* Yamaha products */ product YAMAHA UX256 0x1000 UX256 MIDI I/F product YAMAHA UX96 0x1008 UX96 MIDI I/F product YAMAHA RTA54I 0x4000 NetVolante RTA54i Broadband&ISDN Router product YAMAHA RTA55I 0x4004 NetVolante RTA55i Broadband VoIP Router product YAMAHA RTW65B 0x4001 NetVolante RTW65b Broadband Wireless Router product YAMAHA RTW65I 0x4002 NetVolante RTW65i Broadband&ISDN Wireless Router /* Yano products */ product YANO U640MO 0x0101 U640MO-03 /* Zinwell products */ product ZINWELL RT2570 0x0260 RT2570 /* Zoom Telephonics, Inc. products */ product ZOOM 2986L 0x9700 2986L Fax modem /* ZyDAS Technology products */ product ZYDAS ZD1211 0x1211 ZD1211 /* ZyXEL Communication Co. products */ product ZYXEL OMNI56K 0x1500 Omni 56K Plus product ZYXEL 980N 0x2011 Scorpion-980N keyboard product ZYXEL ZYAIRG220 0x3401 ZyAIR G-220 pwcbsd/usb/usbdi.h000644 000423 000000 00000003421 10551670754 014660 0ustar00luigiwheel000000 000000 #include #if !defined USBDI_H #define USBDI_H #ifdef OLD_USB_COMPAT /* XXX from usbdi.h */ typedef struct usbd_xfer *usbd_xfer_handle; typedef struct usbd_device *usbd_device_handle; typedef struct usbd_interface *usbd_interface_handle; typedef struct usbd_pipe usbd_pipe_handle; /* XXX */ typedef void *usbd_private_handle; typedef void (*usbd_callback)(usbd_xfer_handle, usbd_private_handle, usbd_status); /* Request flags */ //usbd_status usbd_endpoint_count(usbd_interface_handle, u_int8_t *); // iface->idesc->bNumEndpoints #define usbd_get_string(udev, si, buf) \ usbreq_get_string_any(udev, si, buf, sizeof(buf)) /*--- todo --- */ #define USBD_NO_COPY 0x01 /* do not copy data to DMA buffer */ usbd_status usbd_open_pipe(usbd_interface_handle, u_int8_t, u_int8_t, usbd_pipe_handle *); usbd_xfer_handle usbd_alloc_xfer(usbd_device_handle); usbd_status usbd_close_pipe(usbd_pipe_handle); usb_endpoint_descriptor_t * usbd_interface2endpoint_descriptor(usbd_interface_handle iface, u_int8_t index); usbd_status usbd_device2interface_handle(usbd_device_handle, u_int8_t, usbd_interface_handle *); void usbd_get_xfer_status(usbd_xfer_handle, usbd_private_handle *, void **, u_int32_t *, usbd_status *); usbd_status usbd_abort_pipe(usbd_pipe_handle); void usbd_setup_isoc_xfer(usbd_xfer_handle, usbd_pipe_handle, usbd_private_handle, u_int16_t *, u_int32_t, u_int16_t, usbd_callback); void *usbd_alloc_buffer(usbd_xfer_handle, u_int32_t); usbd_status usbd_free_xfer(usbd_xfer_handle); usbd_status usbd_transfer(usbd_xfer_handle); #endif /* USBDI_H */ #endif pwcbsd/usb/usbdi_util.h000644 000423 000000 00000000161 10551670754 015713 0ustar00luigiwheel000000 000000 #include #ifndef usbd_devinfo #define usbd_devinfo(u,s,p) usbd_devinfo(u,s,p,1024) #endif pwcbsd/usb/usbdivar.h000644 000423 000000 00000000162 10551670754 015370 0ustar00luigiwheel000000 000000 #include #ifndef usbd_devinfo #define usbd_devinfo(u,s,p) usbd_devinfo(u,s,p,1024) #endif pwcbsd/usb/usbhid.h000644 000423 000000 00000000133 10551670754 015025 0ustar00luigiwheel000000 000000 /* this file is here so that * old software can compile */ #include pwcbsd/usb/uscanner.c000644 000423 000000 00000046110 10551670754 015365 0ustar00luigiwheel000000 000000 /* $NetBSD: uscanner.c,v 1.30 2002/07/11 21:14:36 augustss Exp$ */ /* Also already merged from NetBSD: * $NetBSD: uscanner.c,v 1.33 2002/09/23 05:51:24 simonb Exp $ */ /*- * Copyright (c) 2000 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology * and Nick Hibma (n_hibma@qubesoft.com). * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include "usbdevs.h" __FBSDID("$FreeBSD: src/sys/dev/usb/uscanner.c,v 1.70 2006/04/11 10:44:31 flz Exp $"); /* * uscanner debugging statements. */ #ifdef USB_DEBUG static int uscanner_debug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, uscanner, CTLFLAG_RW, 0, "USB uscanner"); SYSCTL_INT(_hw_usb_uscanner, OID_AUTO, uscanner, CTLFLAG_RW, &uscanner_debug, 0, "uscanner debug level"); #define DPRINTF(n, fmt, ...) do { \ if (uscanner_debug > (n)) { \ printf("%s: " fmt, __FUNCTION__ \ ,## __VA_ARGS__); \ } \ } while (0) #else #define DPRINTF(...) #endif /* * uscanner transfers macros definition. */ #define USCANNER_BSIZE (1 << 16) #define USCANNER_IFQ_MAXLEN 2 #define USCANNER_N_TRANSFER 4 /* * Transfers stallings handling flags definition. */ #define USCANNER_FLAG_READ_STALL 0x01 #define USCANNER_FLAG_WRITE_STALL 0x02 /* * uscanner_info flags definition. */ #define USCANNER_FLAG_KEEP_OPEN 0x04 /* * uscanner driver specific structures. */ struct uscanner_info { struct usb_devno devno; uint8_t flags; }; struct uscanner_softc { struct usb_cdev sc_cdev; struct mtx sc_mtx; device_t sc_dev; struct usbd_xfer * sc_xfer[USCANNER_N_TRANSFER]; uint8_t sc_flags; /* Used to prevent stalls */ }; /* * Prototypes for driver handling routines (sorted by use). */ static device_probe_t uscanner_probe; static device_attach_t uscanner_attach; static device_detach_t uscanner_detach; /* * Prototypes for xfer transfer callbacks. */ static void uscanner_read_callback(struct usbd_xfer *xfer); static void uscanner_read_clear_stall_callback(struct usbd_xfer *xfer); static void uscanner_write_callback(struct usbd_xfer *xfer); static void uscanner_write_clear_stall_callback(struct usbd_xfer *xfer); /* * Prototypes for the character device handling routines. */ static int32_t uscanner_open(struct usb_cdev *cdev, int32_t fflags, int32_t devtype, struct thread *td); static void uscanner_start_read(struct usb_cdev *cdev); static void uscanner_start_write(struct usb_cdev *cdev); static void uscanner_stop_read(struct usb_cdev *cdev); static void uscanner_stop_write(struct usb_cdev *cdev); /* * xfer transfers array. Resolve-stalling callbacks are marked as control * transfers. */ static const struct usbd_config uscanner_config [USCANNER_N_TRANSFER] = { [0] = { .type = UE_BULK, .endpoint = -1, /* any */ .direction = UE_DIR_OUT, .bufsize = USCANNER_BSIZE, .flags = 0, .callback = &uscanner_write_callback, }, [1] = { .type = UE_BULK, .endpoint = -1, /* any */ .direction = UE_DIR_IN, .bufsize = USCANNER_BSIZE, .flags = USBD_SHORT_XFER_OK, .callback = &uscanner_read_callback, }, [2] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .flags = USBD_USE_DMA, .callback = &uscanner_write_clear_stall_callback, .timeout = 1000, }, [3] = { .type = UE_CONTROL, .endpoint = 0x00, .direction = -1, .bufsize = sizeof(usb_device_request_t), .flags = USBD_USE_DMA, .callback = &uscanner_read_clear_stall_callback, .timeout = 1000, }, }; static devclass_t uscanner_devclass; static device_method_t uscanner_methods[] = { DEVMETHOD(device_probe, uscanner_probe), DEVMETHOD(device_attach, uscanner_attach), DEVMETHOD(device_detach, uscanner_detach), { 0, 0 } }; static driver_t uscanner_driver = { .name = "uscanner", .methods = uscanner_methods, .size = sizeof(struct uscanner_softc), }; DRIVER_MODULE(uscanner, uhub, uscanner_driver, uscanner_devclass, usbd_driver_load, 0); MODULE_DEPEND(uscanner, usb, 1, 1, 1); /* * USB scanners probing array. It determines flags too. */ static const struct uscanner_info uscanner_devs[] = { /* Acer */ { { USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_320U }, 0 }, { { USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_640U }, 0 }, { { USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_640BT }, 0 }, { { USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_620U }, 0 }, { { USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_1240U }, 0 }, { { USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_C310U }, 0 }, /* AGFA */ { { USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCAN1236U }, 0 }, { { USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCAN1212U }, 0 }, { { USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCAN1212U2 }, 0 }, { { USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANTOUCH }, 0 }, { { USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE40 }, 0 }, { { USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE50 }, 0 }, { { USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE20 }, 0 }, { { USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE25 }, 0 }, { { USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE26 }, 0 }, { { USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE52 }, 0 }, /* Avision */ { { USB_VENDOR_AVISION, USB_PRODUCT_AVISION_1200U }, 0 }, /* Canon */ { { USB_VENDOR_CANON, USB_PRODUCT_CANON_N656U }, 0 }, { { USB_VENDOR_CANON, USB_PRODUCT_CANON_N676U }, 0 }, { { USB_VENDOR_CANON, USB_PRODUCT_CANON_N1220U }, 0 }, { { USB_VENDOR_CANON, USB_PRODUCT_CANON_D660U }, 0 }, { { USB_VENDOR_CANON, USB_PRODUCT_CANON_N1240U }, 0 }, { { USB_VENDOR_CANON, USB_PRODUCT_CANON_LIDE25 }, 0 }, /* Epson */ { { USB_VENDOR_EPSON, USB_PRODUCT_EPSON_636 }, 0 }, { { USB_VENDOR_EPSON, USB_PRODUCT_EPSON_610 }, 0 }, { { USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1200 }, 0 }, { { USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1240 }, 0 }, { { USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1250 }, 0 }, { { USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1600 }, 0 }, { { USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1640 }, 0 }, { { USB_VENDOR_EPSON, USB_PRODUCT_EPSON_640U }, 0 }, { { USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1650 }, 0 }, { { USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1660 }, 0 }, { { USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1670 }, 0 }, { { USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1260 }, 0 }, { { USB_VENDOR_EPSON, USB_PRODUCT_EPSON_RX425 }, 0 }, { { USB_VENDOR_EPSON, USB_PRODUCT_EPSON_3200 }, USCANNER_FLAG_KEEP_OPEN }, { { USB_VENDOR_EPSON, USB_PRODUCT_EPSON_GT9700F }, USCANNER_FLAG_KEEP_OPEN }, { { USB_VENDOR_EPSON, USB_PRODUCT_EPSON_GT9300UF }, 0 }, { { USB_VENDOR_EPSON, USB_PRODUCT_EPSON_2480 }, 0 }, /* HP */ { { USB_VENDOR_HP, USB_PRODUCT_HP_2200C }, 0 }, { { USB_VENDOR_HP, USB_PRODUCT_HP_3300C }, 0 }, { { USB_VENDOR_HP, USB_PRODUCT_HP_3400CSE }, 0 }, { { USB_VENDOR_HP, USB_PRODUCT_HP_4100C }, 0 }, { { USB_VENDOR_HP, USB_PRODUCT_HP_4200C }, 0 }, { { USB_VENDOR_HP, USB_PRODUCT_HP_4300C }, 0 }, { { USB_VENDOR_HP, USB_PRODUCT_HP_4670V }, 0 }, { { USB_VENDOR_HP, USB_PRODUCT_HP_S20 }, 0 }, { { USB_VENDOR_HP, USB_PRODUCT_HP_5200C }, 0 }, { { USB_VENDOR_HP, USB_PRODUCT_HP_5300C }, 0 }, { { USB_VENDOR_HP, USB_PRODUCT_HP_5400C }, 0 }, { { USB_VENDOR_HP, USB_PRODUCT_HP_6200C }, 0 }, { { USB_VENDOR_HP, USB_PRODUCT_HP_6300C }, 0 }, { { USB_VENDOR_HP, USB_PRODUCT_HP_82x0C }, 0 }, /* Kye */ { { USB_VENDOR_KYE, USB_PRODUCT_KYE_VIVIDPRO }, 0 }, /* Microtek */ { { USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_X6U }, 0 }, { { USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_336CX }, 0 }, { { USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_336CX2 }, 0 }, { { USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_C6 }, 0 }, { { USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_V6USL }, 0 }, { { USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_V6USL2 }, 0 }, { { USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_V6UL }, 0 }, /* Minolta */ { { USB_VENDOR_MINOLTA, USB_PRODUCT_MINOLTA_5400 }, 0 }, /* Mustek */ { { USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_1200CU }, 0 }, { { USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_BEARPAW1200F }, 0 }, { { USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_BEARPAW1200TA }, 0 }, { { USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_600USB }, 0 }, { { USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_600CU }, 0 }, { { USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_1200USB }, 0 }, { { USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_1200UB }, 0 }, { { USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_1200USBPLUS }, 0 }, { { USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_1200CUPLUS }, 0 }, /* National */ { { USB_VENDOR_NATIONAL, USB_PRODUCT_NATIONAL_BEARPAW1200 }, 0 }, { { USB_VENDOR_NATIONAL, USB_PRODUCT_NATIONAL_BEARPAW2400 }, 0 }, /* Nikon */ { { USB_VENDOR_NIKON, USB_PRODUCT_NIKON_LS40 }, 0 }, /* Primax */ { { USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2X300 }, 0 }, { { USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2E300 }, 0 }, { { USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2300 }, 0 }, { { USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2E3002 }, 0 }, { { USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_9600 }, 0 }, { { USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_600U }, 0 }, { { USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_6200 }, 0 }, { { USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_19200 }, 0 }, { { USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_1200U }, 0 }, { { USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G600 }, 0 }, { { USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_636I }, 0 }, { { USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2600 }, 0 }, { { USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2E600 }, 0 }, /* Scanlogic */ { { USB_VENDOR_SCANLOGIC, USB_PRODUCT_SCANLOGIC_336CX }, 0 }, /* Ultima */ { { USB_VENDOR_ULTIMA, USB_PRODUCT_ULTIMA_1200UBPLUS }, 0 }, /* UMAX */ { { USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA1220U }, 0 }, { { USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA1236U }, 0 }, { { USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA2000U }, 0 }, { { USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA2100U }, 0 }, { { USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA2200U }, 0 }, { { USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA3400 }, 0 }, /* Visioneer */ { { USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_3000 }, 0 }, { { USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_5300 }, 0 }, { { USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_7600 }, 0 }, { { USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_6100 }, 0 }, { { USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_6200 }, 0 }, { { USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_8100 }, 0 }, { { USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_8600 }, 0 } }; /* * It returns vendor and product ids. */ static inline const struct uscanner_info * uscanner_lookup(uint16_t v, uint16_t p) { return ((const struct uscanner_info *)usb_lookup(uscanner_devs, v, p)); } /* * uscanner device probing method. */ static int uscanner_probe(device_t dev) { struct usb_attach_arg *uaa; DPRINTF(10, "\n"); uaa = device_get_ivars(dev); if (uaa->iface != NULL) { return (UMATCH_NONE); } return ((uscanner_lookup(uaa->vendor, uaa->product) != NULL) ? UMATCH_VENDOR_PRODUCT : UMATCH_NONE); } /* * uscanner device attaching method. */ static int uscanner_attach(device_t dev) { struct usb_attach_arg *uaa; struct uscanner_softc *sc; struct usbd_device *udev; const char *p_buf[2]; char buf[32]; int32_t unit; int error; uaa = device_get_ivars(dev); sc = device_get_softc(dev); unit = device_get_unit(dev); /* * A first path softc structure filling. sc_cdev and * sc_xfer are filled later with appropriate functions. */ sc->sc_dev = dev; sc->sc_flags = uscanner_lookup(uaa->vendor, uaa->product)->flags; mtx_init(&(sc->sc_mtx), "uscanner mutex", NULL, MTX_DEF | MTX_RECURSE); /* * Announce the device: */ usbd_set_desc(dev, uaa->device); /* * Assume only one interface and check for this. */ udev = uaa->device; if ((error = usbd_set_config_no(udev, 1, 1))) { device_printf(dev, "could not set config number\n"); goto detach; } /* * Setup the transfer. */ if ((error = usbd_transfer_setup(udev, uaa->iface_index, sc->sc_xfer, uscanner_config, USCANNER_N_TRANSFER, sc, &(sc->sc_mtx)))) { device_printf(dev, "could not setup transfers, " "error=%s\n", usbd_errstr(error)); goto detach; } /* * Setup the character device for USB scanner. */ snprintf(buf, sizeof(buf), "uscanner%u", unit); p_buf[0] = buf; p_buf[1] = NULL; sc->sc_cdev.sc_start_read = &uscanner_start_read; sc->sc_cdev.sc_stop_read = &uscanner_stop_read; sc->sc_cdev.sc_start_write = &uscanner_start_write; sc->sc_cdev.sc_stop_write = &uscanner_stop_write; sc->sc_cdev.sc_open = &uscanner_open; sc->sc_cdev.sc_flags |= (USB_CDEV_FLAG_FWD_SHORT| USB_CDEV_FLAG_WAKEUP_RD_IMMED | USB_CDEV_FLAG_WAKEUP_WR_IMMED); if ((error = usb_cdev_attach(&(sc->sc_cdev), sc, &(sc->sc_mtx), p_buf, UID_ROOT, GID_OPERATOR, 0644, USCANNER_BSIZE, USCANNER_IFQ_MAXLEN, USCANNER_BSIZE, USCANNER_IFQ_MAXLEN))) { device_printf(dev, "error setting the " "char device!\n"); goto detach; } return (0); detach: uscanner_detach(dev); return ENOMEM; } /* * uscanner device detaching method. */ static int uscanner_detach(device_t dev) { struct uscanner_softc *sc; sc = device_get_softc(dev); usb_cdev_detach(&(sc->sc_cdev)); usbd_transfer_unsetup(sc->sc_xfer, USCANNER_N_TRANSFER); mtx_destroy(&(sc->sc_mtx)); return (0); } /* * Reading callback. Implemented as an "in" bulk transfer. */ static void uscanner_read_callback(struct usbd_xfer *xfer) { struct uscanner_softc *sc; struct usbd_mbuf *m; sc = xfer->priv_sc; USBD_CHECK_STATUS(xfer); tr_transferred: usb_cdev_put_data(&(sc->sc_cdev), xfer->buffer, xfer->actlen, 1); tr_setup: /* * If reading is in stall, just jump to clear stall callback and * solve the situation. */ if (sc->sc_flags & USCANNER_FLAG_READ_STALL) { usbd_transfer_start(sc->sc_xfer[3]); return; } USBD_IF_POLL(&(sc->sc_cdev).sc_rdq_free, m); if (m) { usbd_start_hardware(xfer); } return; tr_error: if (xfer->error != USBD_CANCELLED) { sc->sc_flags |= USCANNER_FLAG_READ_STALL; usbd_transfer_start(sc->sc_xfer[3]); } return; } /* * Removing stall on reading callback. */ static void uscanner_read_clear_stall_callback(struct usbd_xfer *xfer) { struct uscanner_softc *sc; struct usbd_xfer *xfer_other; sc = xfer->priv_sc; xfer_other = sc->sc_xfer[1]; USBD_CHECK_STATUS(xfer); tr_setup: usbd_clear_stall_tr_setup(xfer, xfer_other); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, xfer_other); sc->sc_flags &= ~USCANNER_FLAG_READ_STALL; usbd_transfer_start(xfer_other); return; tr_error: sc->sc_flags &= ~USCANNER_FLAG_READ_STALL; usb_cdev_put_data_error(&(sc->sc_cdev)); return; } /* * Writing callback. Implemented as an "out" bulk transfer. */ static void uscanner_write_callback(struct usbd_xfer *xfer) { struct uscanner_softc *sc; uint32_t actlen; sc = xfer->priv_sc; USBD_CHECK_STATUS(xfer); tr_setup: tr_transferred: /* * If writing is in stall, just jump to clear stall callback and * solve the situation. */ if (sc->sc_flags & USCANNER_FLAG_WRITE_STALL) { usbd_transfer_start(sc->sc_xfer[2]); return; } /* * Write datas, setup and perform hardware transfer. */ if (usb_cdev_get_data(&(sc->sc_cdev), xfer->buffer, USCANNER_BSIZE, &actlen, 0)) { xfer->length = actlen; usbd_start_hardware(xfer); } return; tr_error: if (xfer->error != USBD_CANCELLED) { sc->sc_flags |= USCANNER_FLAG_WRITE_STALL; usbd_transfer_start(sc->sc_xfer[2]); } return; } /* * Removing stall on writing callback. */ static void uscanner_write_clear_stall_callback(struct usbd_xfer *xfer) { struct uscanner_softc *sc; struct usbd_xfer *xfer_other; sc = xfer->priv_sc; xfer_other = sc->sc_xfer[0]; USBD_CHECK_STATUS(xfer); tr_setup: usbd_clear_stall_tr_setup(xfer, xfer_other); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, xfer_other); sc->sc_flags &= ~USCANNER_FLAG_WRITE_STALL; usbd_transfer_start(xfer_other); return; tr_error: sc->sc_flags &= ~USCANNER_FLAG_WRITE_STALL; usb_cdev_get_data_error(&(sc->sc_cdev)); return; } /* * uscanner character device opening method. */ static int32_t uscanner_open(struct usb_cdev *cdev, int32_t fflags, int32_t devtype, struct thread *td) { struct uscanner_softc *sc; sc = cdev->sc_priv_ptr; if (!(sc->sc_flags & USCANNER_FLAG_KEEP_OPEN)) { if (fflags & FWRITE) { sc->sc_flags |= USCANNER_FLAG_WRITE_STALL; } if (fflags & FREAD) { sc->sc_flags |= USCANNER_FLAG_READ_STALL; } } return (0); } /* * uscanner character device start reading method. */ static void uscanner_start_read(struct usb_cdev *cdev) { struct uscanner_softc *sc; sc = cdev->sc_priv_ptr; usbd_transfer_start(sc->sc_xfer[1]); } /* * uscanner character device start writing method. */ static void uscanner_start_write(struct usb_cdev *cdev) { struct uscanner_softc *sc; sc = cdev->sc_priv_ptr; usbd_transfer_start(sc->sc_xfer[0]); } /* * uscanner character device stop reading method. */ static void uscanner_stop_read(struct usb_cdev *cdev) { struct uscanner_softc *sc; sc = cdev->sc_priv_ptr; usbd_transfer_stop(sc->sc_xfer[3]); usbd_transfer_stop(sc->sc_xfer[1]); } /* * uscanner character device stop writing method. */ static void uscanner_stop_write(struct usb_cdev *cdev) { struct uscanner_softc *sc; sc = cdev->sc_priv_ptr; usbd_transfer_stop(sc->sc_xfer[2]); usbd_transfer_stop(sc->sc_xfer[0]); } pwcbsd/usb/uvisor.c000644 000423 000000 00000051014 10551670754 015075 0ustar00luigiwheel000000 000000 /* $NetBSD: uvisor.c,v 1.9 2001/01/23 14:04:14 augustss Exp $ */ /* $FreeBSD: src/sys/dev/usb/uvisor.c,v 1.25 2005/12/04 07:34:19 bmah Exp $ */ /* Also already merged from NetBSD: * $NetBSD: uvisor.c,v 1.12 2001/11/13 06:24:57 lukem Exp $ * $NetBSD: uvisor.c,v 1.13 2002/02/11 15:11:49 augustss Exp $ * $NetBSD: uvisor.c,v 1.14 2002/02/27 23:00:03 augustss Exp $ * $NetBSD: uvisor.c,v 1.15 2002/06/16 15:01:31 augustss Exp $ * $NetBSD: uvisor.c,v 1.16 2002/07/11 21:14:36 augustss Exp $ * $NetBSD: uvisor.c,v 1.17 2002/08/13 11:38:15 augustss Exp $ * $NetBSD: uvisor.c,v 1.18 2003/02/05 00:50:14 augustss Exp $ * $NetBSD: uvisor.c,v 1.19 2003/02/07 18:12:37 augustss Exp $ * $NetBSD: uvisor.c,v 1.20 2003/04/11 01:30:10 simonb Exp $ */ /*- * Copyright (c) 2000 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * Handspring Visor (Palmpilot compatible PDA) driver */ #include #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" #ifdef USB_DEBUG #define DPRINTF(n,fmt,...) \ do { if (uvisor_debug > (n)) { \ printf("%s: " fmt, __FUNCTION__,## __VA_ARGS__); } } while (0) static int uvisor_debug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, uvisor, CTLFLAG_RW, 0, "USB uvisor"); SYSCTL_INT(_hw_usb_uvisor, OID_AUTO, debug, CTLFLAG_RW, &uvisor_debug, 0, "uvisor debug level"); #else #define DPRINTF(...) #endif #define UVISOR_CONFIG_INDEX 0 #define UVISOR_IFACE_INDEX 0 #define UVISOR_MODVER 1 #define UVISOR_N_TRANSFER 5 /* units */ #define UVISOR_BUFSIZE 1024 /* bytes */ /* From the Linux driver */ /* * UVISOR_REQUEST_BYTES_AVAILABLE asks the visor for the number of bytes that * are available to be transfered to the host for the specified endpoint. * Currently this is not used, and always returns 0x0001 */ #define UVISOR_REQUEST_BYTES_AVAILABLE 0x01 /* * UVISOR_CLOSE_NOTIFICATION is set to the device to notify it that the host * is now closing the pipe. An empty packet is sent in response. */ #define UVISOR_CLOSE_NOTIFICATION 0x02 /* * UVISOR_GET_CONNECTION_INFORMATION is sent by the host during enumeration to * get the endpoints used by the connection. */ #define UVISOR_GET_CONNECTION_INFORMATION 0x03 /* * UVISOR_GET_CONNECTION_INFORMATION returns data in the following format */ #define UVISOR_MAX_CONN 8 struct uvisor_connection_info { uWord num_ports; struct { uByte port_function_id; uByte port; } UPACKED connections[UVISOR_MAX_CONN]; } UPACKED; #define UVISOR_CONNECTION_INFO_SIZE 18 /* struct uvisor_connection_info.connection[x].port defines: */ #define UVISOR_ENDPOINT_1 0x01 #define UVISOR_ENDPOINT_2 0x02 /* struct uvisor_connection_info.connection[x].port_function_id defines: */ #define UVISOR_FUNCTION_GENERIC 0x00 #define UVISOR_FUNCTION_DEBUGGER 0x01 #define UVISOR_FUNCTION_HOTSYNC 0x02 #define UVISOR_FUNCTION_CONSOLE 0x03 #define UVISOR_FUNCTION_REMOTE_FILE_SYS 0x04 /* * Unknown PalmOS stuff. */ #define UVISOR_GET_PALM_INFORMATION 0x04 #define UVISOR_GET_PALM_INFORMATION_LEN 0x44 struct uvisor_palm_connection_info { uByte num_ports; uByte endpoint_numbers_different; uWord reserved1; struct { uDWord port_function_id; uByte port; uByte end_point_info; uWord reserved; } UPACKED connections[UVISOR_MAX_CONN]; } UPACKED; struct uvisor_softc { struct ucom_softc sc_ucom; struct usbd_xfer * sc_xfer[UVISOR_N_TRANSFER]; u_int16_t sc_flag; #define UVISOR_FLAG_PALM4 0x0001 #define UVISOR_FLAG_VISOR 0x0002 #define UVISOR_FLAG_PALM35 0x0004 #define UVISOR_FLAG_SEND_NOTIFY 0x0008 #define UVISOR_FLAG_WRITE_STALL 0x0010 #define UVISOR_FLAG_READ_STALL 0x0020 u_int8_t sc_iface_no; u_int8_t sc_iface_index; }; struct uvisor_product { u_int16_t vendor; u_int16_t product; u_int16_t flags; }; static const struct uvisor_product * uvisor_find_up(struct usb_attach_arg *uaa); static device_probe_t uvisor_probe; static device_attach_t uvisor_attach; static device_detach_t uvisor_detach; usbd_status uvisor_init(struct uvisor_softc *sc, struct usbd_device *udev, struct usbd_config *config); static int uvisor_open(struct ucom_softc *ucom); static void uvisor_close(struct ucom_softc *ucom); static void uvisor_start_read(struct ucom_softc *ucom); static void uvisor_stop_read(struct ucom_softc *ucom); static void uvisor_start_write(struct ucom_softc *ucom); static void uvisor_stop_write(struct ucom_softc *ucom); static void uvisor_write_callback(struct usbd_xfer *xfer); static void uvisor_write_clear_stall_callback(struct usbd_xfer *xfer); static void uvisor_read_callback(struct usbd_xfer *xfer); static void uvisor_read_clear_stall_callback(struct usbd_xfer *xfer); static void usbd_close_notify_callback(struct usbd_xfer *xfer); static const struct usbd_config uvisor_config[UVISOR_N_TRANSFER] = { [0] = { .type = UE_BULK, .endpoint = -1, /* any */ .direction = UE_DIR_OUT, .bufsize = UVISOR_BUFSIZE, /* bytes */ .flags = 0, .callback = &uvisor_write_callback, }, [1] = { .type = UE_BULK, .endpoint = -1, /* any */ .direction = UE_DIR_IN, .bufsize = UVISOR_BUFSIZE, /* bytes */ .flags = USBD_SHORT_XFER_OK, .callback = &uvisor_read_callback, }, [2] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .callback = &uvisor_write_clear_stall_callback, .timeout = 1000, /* 1 second */ }, [3] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .callback = &uvisor_read_clear_stall_callback, .timeout = 1000, /* 1 second */ }, [4] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = (sizeof(usb_device_request_t) + sizeof(struct uvisor_connection_info)), .flags = USBD_SHORT_XFER_OK, .callback = &usbd_close_notify_callback, .timeout = 1000, /* 1 second */ }, }; static const struct ucom_callback uvisor_callback = { .ucom_open = &uvisor_open, .ucom_close = &uvisor_close, .ucom_start_read = &uvisor_start_read, .ucom_stop_read = &uvisor_stop_read, .ucom_start_write = &uvisor_start_write, .ucom_stop_write = &uvisor_stop_write, }; static device_method_t uvisor_methods[] = { DEVMETHOD(device_probe, uvisor_probe), DEVMETHOD(device_attach, uvisor_attach), DEVMETHOD(device_detach, uvisor_detach), { 0, 0 } }; static devclass_t uvisor_devclass; static driver_t uvisor_driver = { .name = "uvisor", .methods = uvisor_methods, .size = sizeof (struct uvisor_softc), }; DRIVER_MODULE(uvisor, uhub, uvisor_driver, uvisor_devclass, usbd_driver_load, 0); MODULE_DEPEND(uvisor, usb, 1, 1, 1); MODULE_DEPEND(uvisor, ucom, UCOM_MINVER, UCOM_PREFVER, UCOM_MAXVER); MODULE_VERSION(uvisor, UVISOR_MODVER); static const struct uvisor_product uvisor_products[] = { { USB_VENDOR_HANDSPRING, USB_PRODUCT_HANDSPRING_VISOR, UVISOR_FLAG_VISOR }, { USB_VENDOR_HANDSPRING, USB_PRODUCT_HANDSPRING_TREO, UVISOR_FLAG_PALM4 }, { USB_VENDOR_HANDSPRING, USB_PRODUCT_HANDSPRING_TREO600, UVISOR_FLAG_PALM4 }, { USB_VENDOR_PALM, USB_PRODUCT_PALM_M500, UVISOR_FLAG_PALM4 }, { USB_VENDOR_PALM, USB_PRODUCT_PALM_M505, UVISOR_FLAG_PALM4 }, { USB_VENDOR_PALM, USB_PRODUCT_PALM_M515, UVISOR_FLAG_PALM4 }, { USB_VENDOR_PALM, USB_PRODUCT_PALM_I705, UVISOR_FLAG_PALM4 }, { USB_VENDOR_PALM, USB_PRODUCT_PALM_M125, UVISOR_FLAG_PALM4 }, { USB_VENDOR_PALM, USB_PRODUCT_PALM_M130, UVISOR_FLAG_PALM4 }, { USB_VENDOR_PALM, USB_PRODUCT_PALM_TUNGSTEN_Z, UVISOR_FLAG_PALM4 }, { USB_VENDOR_PALM, USB_PRODUCT_PALM_TUNGSTEN_T, UVISOR_FLAG_PALM4 }, { USB_VENDOR_PALM, USB_PRODUCT_PALM_ZIRE, UVISOR_FLAG_PALM4 }, { USB_VENDOR_PALM, USB_PRODUCT_PALM_ZIRE31, UVISOR_FLAG_PALM4 }, { USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_40, 0 }, { USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_41, UVISOR_FLAG_PALM4 }, { USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_S360, UVISOR_FLAG_PALM4 }, { USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_NX60, UVISOR_FLAG_PALM4 }, { USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_35, UVISOR_FLAG_PALM35 }, /* { USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_25, UVISOR_FLAG_PALM4 }, */ { USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_TJ37, UVISOR_FLAG_PALM4 }, { 0, 0, 0 } }; static const struct uvisor_product * uvisor_find_up(struct usb_attach_arg *uaa) { const struct uvisor_product *up = uvisor_products; if (uaa->iface == NULL) { while (up->vendor) { if ((up->vendor == uaa->vendor) && (up->product == uaa->product)) { return up; } up++; } } return NULL; } static int uvisor_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); const struct uvisor_product *up = uvisor_find_up(uaa); return (up ? UMATCH_VENDOR_PRODUCT : UMATCH_NONE); } static int uvisor_attach(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct uvisor_softc *sc = device_get_softc(dev); const struct uvisor_product *up = uvisor_find_up(uaa); struct usbd_interface *iface; struct usbd_config uvisor_config_copy[UVISOR_N_TRANSFER]; usb_interface_descriptor_t *id; int error; DPRINTF(0, "sc=%p\n", sc); bcopy(uvisor_config, uvisor_config_copy, sizeof(uvisor_config_copy)); if (sc == NULL) { return ENOMEM; } usbd_set_desc(dev, uaa->device); /* configure the device */ error = usbd_set_config_index(uaa->device, UVISOR_CONFIG_INDEX, 1); if (error) { DPRINTF(0, "failed to set configuration, error=%s\n", usbd_errstr(error)); goto detach; } iface = usbd_get_iface(uaa->device, UVISOR_IFACE_INDEX); if (iface == NULL) { DPRINTF(0, "no interface\n"); goto detach; } id = usbd_get_interface_descriptor(iface); if (id == NULL) { DPRINTF(0, "no interface descriptor\n"); goto detach; } sc->sc_flag = up->flags; sc->sc_iface_no = id->bInterfaceNumber; sc->sc_iface_index = UVISOR_IFACE_INDEX; error = uvisor_init(sc, uaa->device, uvisor_config_copy); if (error) { DPRINTF(0, "init failed, error=%s\n", usbd_errstr(error)); goto detach; } error = usbd_transfer_setup(uaa->device, sc->sc_iface_index, sc->sc_xfer, uvisor_config_copy, UVISOR_N_TRANSFER, sc, &Giant); if (error) { DPRINTF(0, "could not allocate all pipes\n"); goto detach; } error = ucom_attach(&(sc->sc_ucom), 1, sc, &uvisor_callback, &Giant); if (error) { DPRINTF(0, "ucom_attach failed\n"); goto detach; } return 0; detach: uvisor_detach(dev); return ENXIO; } static int uvisor_detach(device_t dev) { struct uvisor_softc *sc = device_get_softc(dev); DPRINTF(0, "sc=%p\n", sc); ucom_detach(&(sc->sc_ucom), 1); usbd_transfer_unsetup(sc->sc_xfer, UVISOR_N_TRANSFER); return 0; } usbd_status uvisor_init(struct uvisor_softc *sc, struct usbd_device *udev, struct usbd_config *config) { usbd_status err = 0; usb_device_request_t req; struct uvisor_connection_info coninfo; struct uvisor_palm_connection_info pconinfo; int actlen; uWord wAvail; u_int8_t buffer[256]; if (sc->sc_flag & UVISOR_FLAG_VISOR) { DPRINTF(0, "getting connection info\n"); req.bmRequestType = UT_READ_VENDOR_ENDPOINT; req.bRequest = UVISOR_GET_CONNECTION_INFORMATION; USETW(req.wValue, 0); USETW(req.wIndex, 0); USETW(req.wLength, UVISOR_CONNECTION_INFO_SIZE); err = usbd_do_request_flags(udev, &req, &coninfo, USBD_SHORT_XFER_OK, &actlen, USBD_DEFAULT_TIMEOUT); if (err) { goto done; } } #ifdef USB_DEBUG if (sc->sc_flag & UVISOR_FLAG_VISOR) { u_int16_t i, np; const char *desc; np = UGETW(coninfo.num_ports); if (np > UVISOR_MAX_CONN) { np = UVISOR_MAX_CONN; } DPRINTF(0, "Number of ports: %d\n", np); for (i = 0; i < np; ++i) { switch (coninfo.connections[i].port_function_id) { case UVISOR_FUNCTION_GENERIC: desc = "Generic"; break; case UVISOR_FUNCTION_DEBUGGER: desc = "Debugger"; break; case UVISOR_FUNCTION_HOTSYNC: desc = "HotSync"; break; case UVISOR_FUNCTION_REMOTE_FILE_SYS: desc = "Remote File System"; break; default: desc = "unknown"; break; } DPRINTF(0, "Port %d is for %s\n", coninfo.connections[i].port, desc); } } #endif if (sc->sc_flag & UVISOR_FLAG_PALM4) { u_int8_t port; /* Palm OS 4.0 Hack */ req.bmRequestType = UT_READ_VENDOR_ENDPOINT; req.bRequest = UVISOR_GET_PALM_INFORMATION; USETW(req.wValue, 0); USETW(req.wIndex, 0); USETW(req.wLength, UVISOR_GET_PALM_INFORMATION_LEN); err = usbd_do_request_flags(udev, &req, &pconinfo, USBD_SHORT_XFER_OK, &actlen, USBD_DEFAULT_TIMEOUT); if (err) { goto done; } if (actlen < 12) { DPRINTF(0, "too little data\n"); err = USBD_INVAL; goto done; } if (pconinfo.endpoint_numbers_different) { port = pconinfo.connections[0].end_point_info; config[0].endpoint = (port & 0xF); /* output */ config[1].endpoint = (port >> 4); /* input */ } else { port = pconinfo.connections[0].port; config[0].endpoint = (port & 0xF); /* output */ config[1].endpoint = (port & 0xF); /* input */ } #if 0 req.bmRequestType = UT_READ_VENDOR_ENDPOINT; req.bRequest = UVISOR_GET_PALM_INFORMATION; USETW(req.wValue, 0); USETW(req.wIndex, 0); USETW(req.wLength, UVISOR_GET_PALM_INFORMATION_LEN); err = usbd_do_request(udev, &req, buffer); if (err) { goto done; } #endif } if (sc->sc_flag & UVISOR_FLAG_PALM35) { /* get the config number */ DPRINTF(0, "getting config info\n"); req.bmRequestType = UT_READ; req.bRequest = UR_GET_CONFIG; USETW(req.wValue, 0); USETW(req.wIndex, 0); USETW(req.wLength, 1); err = usbd_do_request(udev, &req, buffer); if (err) { goto done; } /* get the interface number */ DPRINTF(0, "get the interface number\n"); req.bmRequestType = UT_READ_DEVICE; req.bRequest = UR_GET_INTERFACE; USETW(req.wValue, 0); USETW(req.wIndex, 0); USETW(req.wLength, 1); err = usbd_do_request(udev, &req, buffer); if (err) { goto done; } } DPRINTF(0, "getting available bytes\n"); req.bmRequestType = UT_READ_VENDOR_ENDPOINT; req.bRequest = UVISOR_REQUEST_BYTES_AVAILABLE; USETW(req.wValue, 0); USETW(req.wIndex, 5); USETW(req.wLength, sizeof(wAvail)); err = usbd_do_request(udev, &req, &wAvail); if (err) { goto done; } DPRINTF(0, "avail=%d\n", UGETW(wAvail)); DPRINTF(0, "done\n"); done: return err; } static int uvisor_open(struct ucom_softc *ucom) { struct uvisor_softc *sc = ucom->sc_parent; /* clear stall first */ sc->sc_flag |= (UVISOR_FLAG_WRITE_STALL| UVISOR_FLAG_READ_STALL); return 0; } static void uvisor_close(struct ucom_softc *ucom) { struct uvisor_softc *sc = ucom->sc_parent; sc->sc_flag |= UVISOR_FLAG_SEND_NOTIFY; usbd_transfer_start(sc->sc_xfer[4]); return; } static void uvisor_start_read(struct ucom_softc *ucom) { struct uvisor_softc *sc = ucom->sc_parent; usbd_transfer_start(sc->sc_xfer[1]); return; } static void uvisor_stop_read(struct ucom_softc *ucom) { struct uvisor_softc *sc = ucom->sc_parent; usbd_transfer_stop(sc->sc_xfer[3]); usbd_transfer_stop(sc->sc_xfer[1]); return; } static void uvisor_start_write(struct ucom_softc *ucom) { struct uvisor_softc *sc = ucom->sc_parent; usbd_transfer_start(sc->sc_xfer[0]); return; } static void uvisor_stop_write(struct ucom_softc *ucom) { struct uvisor_softc *sc = ucom->sc_parent; usbd_transfer_stop(sc->sc_xfer[2]); usbd_transfer_stop(sc->sc_xfer[0]); return; } static void uvisor_write_callback(struct usbd_xfer *xfer) { struct uvisor_softc *sc = xfer->priv_sc; u_int32_t actlen; USBD_CHECK_STATUS(xfer); tr_error: if (xfer->error != USBD_CANCELLED) { sc->sc_flag |= UVISOR_FLAG_WRITE_STALL; usbd_transfer_start(sc->sc_xfer[2]); } return; tr_setup: tr_transferred: if (sc->sc_flag & UVISOR_FLAG_WRITE_STALL) { usbd_transfer_start(sc->sc_xfer[2]); return; } if(ucom_get_data(&(sc->sc_ucom), xfer->buffer, UVISOR_BUFSIZE, &actlen)) { xfer->length = actlen; usbd_start_hardware(xfer); } return; } static void uvisor_write_clear_stall_callback(struct usbd_xfer *xfer) { struct uvisor_softc *sc = xfer->priv_sc; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, sc->sc_xfer[0]); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, sc->sc_xfer[0]); sc->sc_flag &= ~UVISOR_FLAG_WRITE_STALL; usbd_transfer_start(sc->sc_xfer[0]); return; tr_error: sc->sc_flag &= ~UVISOR_FLAG_WRITE_STALL; DPRINTF(0, "clear stall failed, error=%s\n", usbd_errstr(xfer->error)); return; } static void uvisor_read_callback(struct usbd_xfer *xfer) { struct uvisor_softc *sc = xfer->priv_sc; USBD_CHECK_STATUS(xfer); tr_error: if (xfer->error != USBD_CANCELLED) { sc->sc_flag |= UVISOR_FLAG_READ_STALL; usbd_transfer_start(sc->sc_xfer[3]); } return; tr_transferred: ucom_put_data(&(sc->sc_ucom), xfer->buffer, xfer->actlen); tr_setup: if (sc->sc_flag & UVISOR_FLAG_READ_STALL) { usbd_transfer_start(sc->sc_xfer[3]); } else { usbd_start_hardware(xfer); } return; } static void uvisor_read_clear_stall_callback(struct usbd_xfer *xfer) { struct uvisor_softc *sc = xfer->priv_sc; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, sc->sc_xfer[1]); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, sc->sc_xfer[1]); sc->sc_flag &= ~UVISOR_FLAG_READ_STALL; usbd_transfer_start(sc->sc_xfer[1]); return; tr_error: sc->sc_flag &= ~UVISOR_FLAG_READ_STALL; DPRINTF(0, "clear stall failed, error=%s\n", usbd_errstr(xfer->error)); return; } static void usbd_close_notify_callback(struct usbd_xfer *xfer) { usb_device_request_t *req = xfer->buffer; struct uvisor_softc *sc = xfer->priv_sc; USBD_CHECK_STATUS(xfer); tr_error: DPRINTF(0, "error=%s\n", usbd_errstr(xfer->error)); tr_transferred: tr_setup: if (sc->sc_flag & UVISOR_FLAG_SEND_NOTIFY) { sc->sc_flag &= ~UVISOR_FLAG_SEND_NOTIFY; req->bmRequestType = UT_READ_VENDOR_ENDPOINT; /* XXX read? */ req->bRequest = UVISOR_CLOSE_NOTIFICATION; USETW(req->wValue, 0); USETW(req->wIndex, 0); USETW(req->wLength, UVISOR_CONNECTION_INFO_SIZE); usbd_start_hardware(xfer); } return; } pwcbsd/usb/uvscom.c000644 000423 000000 00000060330 10551670754 015063 0ustar00luigiwheel000000 000000 /* $NetBSD: usb/uvscom.c,v 1.1 2002/03/19 15:08:42 augustss Exp $ */ /*- * Copyright (c) 2001-2003, 2005 Shunsuke Akiyama . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ /* * uvscom: SUNTAC Slipper U VS-10U driver. * Slipper U is a PC Card to USB converter for data communication card * adapter. It supports DDI Pocket's Air H" C@rd, C@rd H" 64, NTT's P-in, * P-in m@ater and various data communication card adapters. */ #include "opt_uvscom.h" /* XXX remove this */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" __FBSDID("$FreeBSD: src/sys/dev/usb/uvscom.c $"); #ifdef USB_DEBUG #define DPRINTF(n,fmt,...) \ do { if (uvscom_debug > (n)) { \ printf("%s: " fmt, __FUNCTION__,## __VA_ARGS__); } } while (0) static int uvscom_debug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, uvscom, CTLFLAG_RW, 0, "USB uvscom"); SYSCTL_INT(_hw_usb_uvscom, OID_AUTO, debug, CTLFLAG_RW, &uvscom_debug, 0, "uvscom debug level"); #else #define DPRINTF(...) #endif #define UVSCOM_MODVER 1 /* module version */ #define UVSCOM_CONFIG_INDEX 0 #define UVSCOM_IFACE_INDEX 0 #ifndef UVSCOM_INTR_INTERVAL #define UVSCOM_INTR_INTERVAL 0 /* default */ #endif #define UVSCOM_UNIT_WAIT 5 /* Request */ #define UVSCOM_SET_SPEED 0x10 #define UVSCOM_LINE_CTL 0x11 #define UVSCOM_SET_PARAM 0x12 #define UVSCOM_READ_STATUS 0xd0 #define UVSCOM_SHUTDOWN 0xe0 /* UVSCOM_SET_SPEED parameters */ #define UVSCOM_SPEED_150BPS 0x00 #define UVSCOM_SPEED_300BPS 0x01 #define UVSCOM_SPEED_600BPS 0x02 #define UVSCOM_SPEED_1200BPS 0x03 #define UVSCOM_SPEED_2400BPS 0x04 #define UVSCOM_SPEED_4800BPS 0x05 #define UVSCOM_SPEED_9600BPS 0x06 #define UVSCOM_SPEED_19200BPS 0x07 #define UVSCOM_SPEED_38400BPS 0x08 #define UVSCOM_SPEED_57600BPS 0x09 #define UVSCOM_SPEED_115200BPS 0x0a /* UVSCOM_LINE_CTL parameters */ #define UVSCOM_BREAK 0x40 #define UVSCOM_RTS 0x02 #define UVSCOM_DTR 0x01 #define UVSCOM_LINE_INIT 0x08 /* UVSCOM_SET_PARAM parameters */ #define UVSCOM_DATA_MASK 0x03 #define UVSCOM_DATA_BIT_8 0x03 #define UVSCOM_DATA_BIT_7 0x02 #define UVSCOM_DATA_BIT_6 0x01 #define UVSCOM_DATA_BIT_5 0x00 #define UVSCOM_STOP_MASK 0x04 #define UVSCOM_STOP_BIT_2 0x04 #define UVSCOM_STOP_BIT_1 0x00 #define UVSCOM_PARITY_MASK 0x18 #define UVSCOM_PARITY_EVEN 0x18 #if 0 #define UVSCOM_PARITY_UNK 0x10 #endif #define UVSCOM_PARITY_ODD 0x08 #define UVSCOM_PARITY_NONE 0x00 /* Status bits */ #define UVSCOM_TXRDY 0x04 #define UVSCOM_RXRDY 0x01 #define UVSCOM_DCD 0x08 #define UVSCOM_NOCARD 0x04 #define UVSCOM_DSR 0x02 #define UVSCOM_CTS 0x01 #define UVSCOM_USTAT_MASK (UVSCOM_NOCARD | UVSCOM_DSR | UVSCOM_CTS) /* * These are the maximum number of bytes transferred per frame. * The output buffer size cannot be increased due to the size encoding. */ #define UVSCOM_IBUFSIZE 512 /* bytes */ #define UVSCOM_OBUFSIZE 64 /* bytes */ #ifndef UVSCOM_DEFAULT_OPKTSIZE #define UVSCOM_DEFAULT_OPKTSIZE 8 #endif #define UVSCOM_N_TRANSFER 10 struct uvscom_softc { struct ucom_softc sc_ucom; struct __callout sc_watchdog; struct usbd_xfer * sc_xfer[UVSCOM_N_TRANSFER]; u_int16_t sc_line_ctrl; /* line control register */ u_int16_t sc_line_speed; /* line speed */ u_int16_t sc_line_param; /* line parameters */ u_int16_t sc_flag; #define UVSCOM_FLAG_WAIT_USB 0x0001 #define UVSCOM_FLAG_WRITE_STALL 0x0002 #define UVSCOM_FLAG_READ_STALL 0x0004 #define UVSCOM_FLAG_INTR_STALL 0x0008 #define UVSCOM_FLAG_OPEN 0x0010 #define UVSCOM_FLAG_SET_LINE 0x0020 #define UVSCOM_FLAG_SET_LINE_SPEED 0x0040 #define UVSCOM_FLAG_SET_LINE_PARM 0x0080 u_int8_t sc_iface_no; /* interface number */ u_int8_t sc_iface_index; /* interface index */ u_int8_t sc_dtr; /* current DTR state */ u_int8_t sc_rts; /* current RTS state */ u_int8_t sc_lsr; /* local status register */ u_int8_t sc_msr; /* uvscom status register */ u_int8_t sc_unit_status; /* unit status */ }; static device_probe_t uvscom_probe; static device_attach_t uvscom_attach; static device_detach_t uvscom_detach; static void uvscom_watchdog(void *arg); static void uvscom_write_callback(struct usbd_xfer *xfer); static void uvscom_write_clear_stall_callback(struct usbd_xfer *xfer); static void uvscom_read_callback(struct usbd_xfer *xfer); static void uvscom_read_clear_stall_callback(struct usbd_xfer *xfer); static void uvscom_intr_callback(struct usbd_xfer *xfer); static void uvscom_intr_clear_stall_callback(struct usbd_xfer *xfer); static void uvscom_read_status_callback(struct usbd_xfer *xfer); static void uvscom_shutdown_callback(struct usbd_xfer *xfer); static void uvscom_set_line_callback(struct usbd_xfer *xfer); static void uvscom_set_line_coding_callback(struct usbd_xfer *xfer); static void uvscom_set_dtr(struct ucom_softc *ucom, u_int8_t onoff); static void uvscom_set_rts(struct ucom_softc *ucom, u_int8_t onoff); static void uvscom_set_break(struct ucom_softc *ucom, u_int8_t onoff); static int uvscom_param(struct ucom_softc *ucom, struct termios *t); static int uvscom_open(struct ucom_softc *ucom); static void uvscom_close(struct ucom_softc *ucom); static void uvscom_start_read(struct ucom_softc *ucom); static void uvscom_stop_read(struct ucom_softc *ucom); static void uvscom_start_write(struct ucom_softc *ucom); static void uvscom_stop_write(struct ucom_softc *ucom); static void uvscom_get_status(struct ucom_softc *ucom, u_int8_t *lsr, u_int8_t *msr); static int uvscom_ioctl(struct ucom_softc *ucom, uint32_t cmd, caddr_t data, int fflag, struct thread *td); static const struct usbd_config uvscom_config[UVSCOM_N_TRANSFER] = { [0] = { .type = UE_BULK, .endpoint = -1, /* any */ .direction = UE_DIR_OUT, .bufsize = UVSCOM_OBUFSIZE, .flags = 0, .callback = &uvscom_write_callback, }, [1] = { .type = UE_BULK, .endpoint = -1, /* any */ .direction = UE_DIR_IN, .bufsize = UVSCOM_IBUFSIZE, .flags = USBD_SHORT_XFER_OK, .callback = &uvscom_read_callback, }, [2] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .callback = &uvscom_write_clear_stall_callback, .timeout = 1000, /* 1 second */ }, [3] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .callback = &uvscom_read_clear_stall_callback, .timeout = 1000, /* 1 second */ }, [4] = { .type = UE_INTERRUPT, .endpoint = -1, /* any */ .direction = UE_DIR_IN, .flags = USBD_SHORT_XFER_OK, .bufsize = 0, /* use wMaxPacketSize */ .callback = &uvscom_intr_callback, }, [5] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .callback = &uvscom_intr_clear_stall_callback, .timeout = 1000, /* 1 second */ }, [6] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t) + 2, .callback = &uvscom_read_status_callback, .timeout = 1000, /* 1 second */ }, [7] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .callback = &uvscom_shutdown_callback, .timeout = 1000, /* 1 second */ }, [8] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .callback = &uvscom_set_line_callback, .timeout = 1000, /* 1 second */ }, [9] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = -1, .bufsize = sizeof(usb_device_request_t), .callback = &uvscom_set_line_coding_callback, .timeout = 1000, /* 1 second */ }, }; static const struct ucom_callback uvscom_callback = { .ucom_get_status = &uvscom_get_status, .ucom_set_dtr = &uvscom_set_dtr, .ucom_set_rts = &uvscom_set_rts, .ucom_set_break = &uvscom_set_break, .ucom_param = &uvscom_param, .ucom_ioctl = &uvscom_ioctl, .ucom_open = &uvscom_open, .ucom_close = &uvscom_close, .ucom_start_read = &uvscom_start_read, .ucom_stop_read = &uvscom_stop_read, .ucom_start_write = &uvscom_start_write, .ucom_stop_write = &uvscom_stop_write, }; static const struct usb_devno uvscom_devs [] = { /* SUNTAC U-Cable type A4 */ { USB_VENDOR_SUNTAC, USB_PRODUCT_SUNTAC_AS144L4 }, /* SUNTAC U-Cable type D2 */ { USB_VENDOR_SUNTAC, USB_PRODUCT_SUNTAC_DS96L }, /* SUNTAC Ir-Trinity */ { USB_VENDOR_SUNTAC, USB_PRODUCT_SUNTAC_IS96U }, /* SUNTAC U-Cable type P1 */ { USB_VENDOR_SUNTAC, USB_PRODUCT_SUNTAC_PS64P1 }, /* SUNTAC Slipper U */ { USB_VENDOR_SUNTAC, USB_PRODUCT_SUNTAC_VS10U }, }; static device_method_t uvscom_methods[] = { DEVMETHOD(device_probe, uvscom_probe), DEVMETHOD(device_attach, uvscom_attach), DEVMETHOD(device_detach, uvscom_detach), { 0, 0 } }; static devclass_t uvscom_devclass; static driver_t uvscom_driver = { .name = "uvscom", .methods = uvscom_methods, .size = sizeof (struct uvscom_softc), }; DRIVER_MODULE(uvscom, uhub, uvscom_driver, uvscom_devclass, usbd_driver_load, 0); MODULE_DEPEND(uvscom, usb, 1, 1, 1); MODULE_DEPEND(uvscom, ucom, UCOM_MINVER, UCOM_PREFVER, UCOM_MAXVER); MODULE_VERSION(uvscom, UVSCOM_MODVER); static int uvscom_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); if (uaa->iface) { return UMATCH_NONE; } return (usb_lookup(uvscom_devs, uaa->vendor, uaa->product) ? UMATCH_VENDOR_PRODUCT : UMATCH_NONE); } static int uvscom_attach(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct uvscom_softc *sc = device_get_softc(dev); usb_interface_descriptor_t *id; struct usbd_interface *iface; int error; if (sc == NULL) { return ENOMEM; } usbd_set_desc(dev, uaa->device); DPRINTF(0, "sc=%p\n", sc); __callout_init_mtx(&(sc->sc_watchdog), &Giant, CALLOUT_RETURNUNLOCKED); /* configure the device */ error = usbd_set_config_index(uaa->device, UVSCOM_CONFIG_INDEX, 1); if (error) { device_printf(dev, "failed to set configuration, " "error=%s\n", usbd_errstr(error)); goto detach; } iface = usbd_get_iface(uaa->device, UVSCOM_IFACE_INDEX); if (iface == NULL) { DPRINTF(0, "no interface\n"); goto detach; } id = usbd_get_interface_descriptor(iface); if (id == NULL) { DPRINTF(0, "no interface descriptor\n"); goto detach; } sc->sc_iface_no = id->bInterfaceNumber; sc->sc_iface_index = UVSCOM_IFACE_INDEX; error = usbd_transfer_setup(uaa->device, sc->sc_iface_index, sc->sc_xfer, uvscom_config, UVSCOM_N_TRANSFER, sc, &Giant); if (error) { DPRINTF(0, "could not allocate all USB transfers!\n"); goto detach; } sc->sc_flag |= UVSCOM_FLAG_WAIT_USB; sc->sc_dtr = -1; sc->sc_rts = -1; sc->sc_line_ctrl = UVSCOM_LINE_INIT; error = ucom_attach(&(sc->sc_ucom), 1, sc, &uvscom_callback, &Giant); if (error) { goto detach; } /* start interrupt pipe */ usbd_transfer_start(sc->sc_xfer[4]); /* start watchdog (returns unlocked) */ mtx_lock(&Giant); uvscom_watchdog(sc); return 0; detach: uvscom_detach(dev); return ENXIO; } static int uvscom_detach(device_t dev) { struct uvscom_softc *sc = device_get_softc(dev); DPRINTF(0, "sc=%p\n", sc); __callout_stop(&(sc->sc_watchdog)); /* stop interrupt pipe */ if (sc->sc_xfer[4]) { usbd_transfer_stop(sc->sc_xfer[4]); } ucom_detach(&(sc->sc_ucom), 1); usbd_transfer_unsetup(sc->sc_xfer, UVSCOM_N_TRANSFER); __callout_drain(&(sc->sc_watchdog)); return 0; } static void uvscom_watchdog(void *arg) { struct uvscom_softc *sc = arg; mtx_assert(&Giant, MA_OWNED); usbd_transfer_start(sc->sc_xfer[6]); __callout_reset(&(sc->sc_watchdog), hz, &(uvscom_watchdog), sc); mtx_unlock(&Giant); return; } static void uvscom_write_callback(struct usbd_xfer *xfer) { struct uvscom_softc *sc = xfer->priv_sc; u_int32_t actlen; USBD_CHECK_STATUS(xfer); tr_error: if (xfer->error != USBD_CANCELLED) { sc->sc_flag |= UVSCOM_FLAG_WRITE_STALL; usbd_transfer_start(sc->sc_xfer[2]); } return; tr_setup: tr_transferred: if (sc->sc_flag & UVSCOM_FLAG_WRITE_STALL) { usbd_transfer_start(sc->sc_xfer[2]); return; } if(ucom_get_data(&(sc->sc_ucom), xfer->buffer, UVSCOM_OBUFSIZE, &actlen)) { xfer->length = actlen; usbd_start_hardware(xfer); } return; } static void uvscom_write_clear_stall_callback(struct usbd_xfer *xfer) { struct uvscom_softc *sc = xfer->priv_sc; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, sc->sc_xfer[0]); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, sc->sc_xfer[0]); sc->sc_flag &= ~UVSCOM_FLAG_WRITE_STALL; usbd_transfer_start(sc->sc_xfer[0]); return; tr_error: sc->sc_flag &= ~UVSCOM_FLAG_WRITE_STALL; DPRINTF(0, "clear stall failed, error=%s\n", usbd_errstr(xfer->error)); return; } static void uvscom_read_callback(struct usbd_xfer *xfer) { struct uvscom_softc *sc = xfer->priv_sc; USBD_CHECK_STATUS(xfer); tr_error: if (xfer->error != USBD_CANCELLED) { sc->sc_flag |= UVSCOM_FLAG_READ_STALL; usbd_transfer_start(sc->sc_xfer[3]); } return; tr_transferred: ucom_put_data(&(sc->sc_ucom), xfer->buffer, xfer->actlen); tr_setup: if (sc->sc_flag & UVSCOM_FLAG_READ_STALL) { usbd_transfer_start(sc->sc_xfer[3]); } else { usbd_start_hardware(xfer); } return; } static void uvscom_read_clear_stall_callback(struct usbd_xfer *xfer) { struct uvscom_softc *sc = xfer->priv_sc; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, sc->sc_xfer[1]); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, sc->sc_xfer[1]); sc->sc_flag &= ~UVSCOM_FLAG_READ_STALL; usbd_transfer_start(sc->sc_xfer[1]); return; tr_error: sc->sc_flag &= ~UVSCOM_FLAG_READ_STALL; DPRINTF(0, "clear stall failed, error=%s\n", usbd_errstr(xfer->error)); return; } static void uvscom_intr_callback(struct usbd_xfer *xfer) { struct uvscom_softc *sc = xfer->priv_sc; u_int8_t *buf = xfer->buffer; USBD_CHECK_STATUS(xfer); tr_error: if (xfer->error != USBD_CANCELLED) { sc->sc_flag |= UVSCOM_FLAG_INTR_STALL; usbd_transfer_start(sc->sc_xfer[5]); } return; tr_transferred: if (xfer->actlen >= 2) { sc->sc_lsr = 0; sc->sc_msr = 0; sc->sc_unit_status = buf[1]; if (buf[0] & UVSCOM_TXRDY) { sc->sc_lsr |= ULSR_TXRDY; } if (buf[0] & UVSCOM_RXRDY) { sc->sc_lsr |= ULSR_RXRDY; } if (buf[1] & UVSCOM_CTS) { sc->sc_msr |= SER_CTS; } if (buf[1] & UVSCOM_DSR) { sc->sc_msr |= SER_DSR; } if (buf[1] & UVSCOM_DCD) { sc->sc_msr |= SER_DCD; } if (sc->sc_flag & UVSCOM_FLAG_OPEN) { ucom_status_change(&(sc->sc_ucom)); } } tr_setup: if (sc->sc_flag & UVSCOM_FLAG_INTR_STALL) { usbd_transfer_start(sc->sc_xfer[5]); } else { usbd_start_hardware(xfer); } return; } static void uvscom_intr_clear_stall_callback(struct usbd_xfer *xfer) { struct uvscom_softc *sc = xfer->priv_sc; USBD_CHECK_STATUS(xfer); tr_setup: /* start clear stall */ usbd_clear_stall_tr_setup(xfer, sc->sc_xfer[4]); return; tr_transferred: usbd_clear_stall_tr_transferred(xfer, sc->sc_xfer[4]); sc->sc_flag &= ~UVSCOM_FLAG_INTR_STALL; usbd_transfer_start(sc->sc_xfer[4]); return; tr_error: sc->sc_flag &= ~UVSCOM_FLAG_INTR_STALL; DPRINTF(0, "clear stall failed, error=%s\n", usbd_errstr(xfer->error)); return; } static void uvscom_read_status_callback(struct usbd_xfer *xfer) { usb_device_request_t *req = xfer->buffer; USBD_CHECK_STATUS(xfer); tr_error: DPRINTF(0, "error=%s\n", usbd_errstr(xfer->error)); tr_transferred: return; tr_setup: req->bmRequestType = UT_READ_VENDOR_DEVICE; req->bRequest = UVSCOM_READ_STATUS; USETW(req->wValue, 0); USETW(req->wIndex, 0); USETW(req->wLength, 2); usbd_start_hardware(xfer); return; } static void uvscom_shutdown_callback(struct usbd_xfer *xfer) { usb_device_request_t *req = xfer->buffer; USBD_CHECK_STATUS(xfer); tr_error: DPRINTF(0, "error=%s\n", usbd_errstr(xfer->error)); tr_transferred: return; tr_setup: req->bmRequestType = UT_WRITE_VENDOR_DEVICE; req->bRequest = UVSCOM_SHUTDOWN; USETW(req->wValue, 0); USETW(req->wIndex, 0); USETW(req->wLength, 0); usbd_start_hardware(xfer); return; } static void uvscom_set_line_callback(struct usbd_xfer *xfer) { usb_device_request_t *req = xfer->buffer; struct uvscom_softc *sc = xfer->priv_sc; USBD_CHECK_STATUS(xfer); tr_error: DPRINTF(0, "error=%s\n", usbd_errstr(xfer->error)); tr_transferred: tr_setup: if (sc->sc_flag & UVSCOM_FLAG_SET_LINE) { sc->sc_flag &= ~UVSCOM_FLAG_SET_LINE; req->bmRequestType = UT_WRITE_VENDOR_DEVICE; req->bRequest = UVSCOM_LINE_CTL; USETW(req->wValue, sc->sc_line_ctrl); USETW(req->wIndex, 0); USETW(req->wLength, 0); usbd_start_hardware(xfer); } return; } static void uvscom_set_line_coding_callback(struct usbd_xfer *xfer) { usb_device_request_t *req = xfer->buffer; struct uvscom_softc *sc = xfer->priv_sc; USBD_CHECK_STATUS(xfer); tr_error: DPRINTF(0, "error=%s\n", usbd_errstr(xfer->error)); tr_transferred: tr_setup: if (sc->sc_flag & UVSCOM_FLAG_SET_LINE_SPEED) { sc->sc_flag &= ~UVSCOM_FLAG_SET_LINE_SPEED; req->bmRequestType = UT_WRITE_VENDOR_DEVICE; req->bRequest = UVSCOM_SET_SPEED; USETW(req->wValue, sc->sc_line_speed); USETW(req->wIndex, 0); USETW(req->wLength, 0); usbd_start_hardware(xfer); return; } if (sc->sc_flag & UVSCOM_FLAG_SET_LINE_PARM) { sc->sc_flag &= ~UVSCOM_FLAG_SET_LINE_PARM; req->bmRequestType = UT_WRITE_VENDOR_DEVICE; req->bRequest = UVSCOM_SET_PARAM; USETW(req->wValue, sc->sc_line_param); USETW(req->wIndex, 0); USETW(req->wLength, 0); usbd_start_hardware(xfer); return; } return; } static void uvscom_set_dtr(struct ucom_softc *ucom, u_int8_t onoff) { struct uvscom_softc *sc = ucom->sc_parent; DPRINTF(0, "onoff = %d\n", onoff); if (sc->sc_dtr != onoff) { sc->sc_dtr = onoff; if (onoff) sc->sc_line_ctrl |= UVSCOM_DTR; else sc->sc_line_ctrl &= ~UVSCOM_DTR; sc->sc_flag |= UVSCOM_FLAG_SET_LINE; usbd_transfer_start(sc->sc_xfer[8]); } return; } static void uvscom_set_rts(struct ucom_softc *ucom, u_int8_t onoff) { struct uvscom_softc *sc = ucom->sc_parent; DPRINTF(0, "onoff = %d\n", onoff); if (sc->sc_rts != onoff) { sc->sc_rts = onoff; if (onoff) sc->sc_line_ctrl |= UVSCOM_RTS; else sc->sc_line_ctrl &= ~UVSCOM_RTS; sc->sc_flag |= UVSCOM_FLAG_SET_LINE; usbd_transfer_start(sc->sc_xfer[8]); } return; } static void uvscom_set_break(struct ucom_softc *ucom, u_int8_t onoff) { struct uvscom_softc *sc = ucom->sc_parent; DPRINTF(0, "onoff = %d\n", onoff); if (onoff) sc->sc_line_ctrl |= UVSCOM_BREAK; else sc->sc_line_ctrl &= ~UVSCOM_BREAK; sc->sc_flag |= UVSCOM_FLAG_SET_LINE; usbd_transfer_start(sc->sc_xfer[8]); return; } static int uvscom_param(struct ucom_softc *ucom, struct termios *t) { struct uvscom_softc *sc = ucom->sc_parent; DPRINTF(0, "\n"); switch (t->c_ospeed) { case B150: sc->sc_line_speed = UVSCOM_SPEED_150BPS; break; case B300: sc->sc_line_speed = UVSCOM_SPEED_300BPS; break; case B600: sc->sc_line_speed = UVSCOM_SPEED_600BPS; break; case B1200: sc->sc_line_speed = UVSCOM_SPEED_1200BPS; break; case B2400: sc->sc_line_speed = UVSCOM_SPEED_2400BPS; break; case B4800: sc->sc_line_speed = UVSCOM_SPEED_4800BPS; break; case B9600: sc->sc_line_speed = UVSCOM_SPEED_9600BPS; break; case B19200: sc->sc_line_speed = UVSCOM_SPEED_19200BPS; break; case B38400: sc->sc_line_speed = UVSCOM_SPEED_38400BPS; break; case B57600: sc->sc_line_speed = UVSCOM_SPEED_57600BPS; break; case B115200: sc->sc_line_speed = UVSCOM_SPEED_115200BPS; break; default: return (EIO); } sc->sc_line_param = 0; /* reset */ sc->sc_line_param |= ((t->c_cflag & CSTOPB) ? UVSCOM_STOP_BIT_2 : UVSCOM_STOP_BIT_1); sc->sc_line_param |= ((t->c_cflag & PARENB) ? ((t->c_cflag & PARODD) ? UVSCOM_PARITY_ODD : UVSCOM_PARITY_EVEN) : UVSCOM_PARITY_NONE); switch (t->c_cflag & CSIZE) { case CS5: sc->sc_line_param |= UVSCOM_DATA_BIT_5; break; case CS6: sc->sc_line_param |= UVSCOM_DATA_BIT_6; break; case CS7: sc->sc_line_param |= UVSCOM_DATA_BIT_7; break; case CS8: sc->sc_line_param |= UVSCOM_DATA_BIT_8; break; default: return EIO; } sc->sc_flag |= (UVSCOM_FLAG_SET_LINE_SPEED| UVSCOM_FLAG_SET_LINE_PARM); usbd_transfer_start(sc->sc_xfer[9]); return 0; } static int uvscom_open(struct ucom_softc *ucom) { struct uvscom_softc *sc = ucom->sc_parent; DPRINTF(0, "sc = %p\n", sc); /* clear stall first */ sc->sc_flag |= (UVSCOM_FLAG_OPEN| UVSCOM_FLAG_WRITE_STALL| UVSCOM_FLAG_READ_STALL); /* check if PC card was inserted */ if (sc->sc_unit_status & UVSCOM_NOCARD) { DPRINTF(0, "no PC card!\n"); return ENXIO; } return 0; } static void uvscom_close(struct ucom_softc *ucom) { struct uvscom_softc *sc = ucom->sc_parent; DPRINTF(0, "sc=%p\n", sc); usbd_transfer_start(sc->sc_xfer[7]); sc->sc_flag &= ~UVSCOM_FLAG_OPEN; return; } static void uvscom_start_read(struct ucom_softc *ucom) { struct uvscom_softc *sc = ucom->sc_parent; usbd_transfer_start(sc->sc_xfer[1]); return; } static void uvscom_stop_read(struct ucom_softc *ucom) { struct uvscom_softc *sc = ucom->sc_parent; usbd_transfer_stop(sc->sc_xfer[3]); usbd_transfer_stop(sc->sc_xfer[1]); return; } static void uvscom_start_write(struct ucom_softc *ucom) { struct uvscom_softc *sc = ucom->sc_parent; usbd_transfer_start(sc->sc_xfer[0]); return; } static void uvscom_stop_write(struct ucom_softc *ucom) { struct uvscom_softc *sc = ucom->sc_parent; usbd_transfer_stop(sc->sc_xfer[2]); usbd_transfer_stop(sc->sc_xfer[0]); return; } static void uvscom_get_status(struct ucom_softc *ucom, u_int8_t *lsr, u_int8_t *msr) { struct uvscom_softc *sc = ucom->sc_parent; if (lsr) { *lsr = sc->sc_lsr; } if (msr) { *msr = sc->sc_msr; } return; } static int uvscom_ioctl(struct ucom_softc *ucom, uint32_t cmd, caddr_t data, int fflag, struct thread *td) { int error = ENOTTY; DPRINTF(0, "cmd = 0x%08x\n", cmd); switch (cmd) { case TIOCNOTTY: case TIOCMGET: case TIOCMSET: break; default: DPRINTF(0, "unknown\n"); error = ENOTTY; break; } return error; }